Commit 0cfc7da3 authored by David Woodhouse's avatar David Woodhouse
parents 1417fc44 20a92fc7
......@@ -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
......
......@@ -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
......
......@@ -150,3 +150,24 @@ the buffer.
Ordering constraints:
Lock wbuf_sem last, after the alloc_sem or and f->sem.
c->xattr_sem
------------
This read/write semaphore protects against concurrent access to the
xattr related objects which include stuff in superblock and ic->xref.
In read-only path, write-semaphore is too much exclusion. It's enough
by read-semaphore. But you must hold write-semaphore when updating,
creating or deleting any xattr related object.
Once xattr_sem released, there would be no assurance for the existence
of those objects. Thus, a series of processes is often required to retry,
when updating such a object is necessary under holding read semaphore.
For example, do_jffs2_getxattr() holds read-semaphore to scan xref and
xdatum at first. But it retries this process with holding write-semaphore
after release read-semaphore, if it's necessary to load name/value pair
from medium.
Ordering constraints:
Lock xattr_sem last, after the alloc_sem.
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2006 NEC Corporation
*
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this 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(struct jffs2_acl_header)
+ count * sizeof(struct jffs2_acl_entry_short);
} else {
return sizeof(struct jffs2_acl_header)
+ 4 * sizeof(struct jffs2_acl_entry_short)
+ (count - 4) * sizeof(struct jffs2_acl_entry);
}
}
static int jffs2_acl_count(size_t size)
{
size_t s;
size -= sizeof(struct jffs2_acl_header);
s = size - 4 * sizeof(struct jffs2_acl_entry_short);
if (s < 0) {
if (size % sizeof(struct jffs2_acl_entry_short))
return -1;
return size / sizeof(struct jffs2_acl_entry_short);
} else {
if (s % sizeof(struct jffs2_acl_entry))
return -1;
return s / sizeof(struct jffs2_acl_entry) + 4;
}
}
static struct posix_acl *jffs2_acl_from_medium(void *value, size_t size)
{
void *end = value + size;
struct jffs2_acl_header *header = value;
struct jffs2_acl_entry *entry;
struct posix_acl *acl;
uint32_t ver;
int i, count;
if (!value)
return NULL;
if (size < sizeof(struct jffs2_acl_header))
return ERR_PTR(-EINVAL);
ver = je32_to_cpu(header->a_version);
if (ver != JFFS2_ACL_VERSION) {
JFFS2_WARNING("Invalid ACL version. (=%u)\n", ver);
return ERR_PTR(-EINVAL);
}
value += sizeof(struct 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++) {
entry = value;
if (value + sizeof(struct 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 += sizeof(struct jffs2_acl_entry_short);
acl->a_entries[i].e_id = ACL_UNDEFINED_ID;
break;
case ACL_USER:
case ACL_GROUP:
value += sizeof(struct jffs2_acl_entry);
if (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)
{
struct jffs2_acl_header *header;
struct jffs2_acl_entry *entry;
void *e;
size_t i;
*size = jffs2_acl_size(acl->a_count);
header = kmalloc(sizeof(*header) + acl->a_count * sizeof(*entry), GFP_KERNEL);
if (!header)
return ERR_PTR(-ENOMEM);
header->a_version = cpu_to_je32(JFFS2_ACL_VERSION);
e = header + 1;
for (i=0; i < acl->a_count; i++) {
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(struct jffs2_acl_entry);
break;
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_MASK:
case ACL_OTHER:
e += sizeof(struct jffs2_acl_entry_short);
break;
default:
goto fail;
}
}
return header;
fail:
kfree(header);
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,
};
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2006 NEC Corporation
*
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
*/
struct jffs2_acl_entry {
jint16_t e_tag;
jint16_t e_perm;
jint32_t e_id;
};
struct jffs2_acl_entry_short {
jint16_t e_tag;
jint16_t e_perm;
};
struct jffs2_acl_header {
jint32_t a_version;
};
#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 */
......@@ -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;
......
......@@ -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
......
......@@ -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) {
......@@ -658,6 +696,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) {
......
......@@ -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 =
......
......@@ -184,7 +184,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)
......@@ -223,6 +228,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);
}
......@@ -508,6 +514,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;
......@@ -542,6 +550,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);
......
......@@ -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,23 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
ic = jffs2_raw_ref_to_ic(raw);
#ifdef CONFIG_JFFS2_FS_XATTR
/* 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. */
if (ic->class == RAWNODE_CLASS_XATTR_DATUM
|| ic->class == RAWNODE_CLASS_XATTR_REF) {
BUG_ON(raw->next_in_ino != (void *)ic);
spin_unlock(&c->erase_completion_lock);
if (ic->class == RAWNODE_CLASS_XATTR_DATUM) {
ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic);
} else {
ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic);
}
goto release_sem;
}
#endif
/* 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. */
......
......@@ -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 */
......@@ -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_unchecked;
struct jffs2_xattr_ref *xref_temp;
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;
};
......
......@@ -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,7 +72,23 @@ 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)
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();
......@@ -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,40 @@ 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;
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
......@@ -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;
}
......
......@@ -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 jffs2_xattr_ref *xref;
#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
/*
......@@ -385,6 +398,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);
......
......@@ -58,6 +58,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
}
......
......@@ -306,6 +306,137 @@ 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->next 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);
ref->next = c->xref_temp;
c->xref_temp = ref;
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
/* Called with 'buf_size == 0' if buf is in fact a pointer _directly_ into
the flash, XIP-style */
static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
......@@ -641,6 +772,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) {
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2006 NEC Corporation
*
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this 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
};
......@@ -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);
......@@ -402,8 +483,101 @@ 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_free_raw_node_ref(raw);
if (PTR_ERR(xd) == -EEXIST) {
/* a newer version of xd exists */
DIRTY_SPACE(je32_to_cpu(spx->totlen));
sp += JFFS2_SUMMARY_XATTR_SIZE;
break;
}
JFFS2_NOTICE("allocation of xattr_datum failed\n");
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;
ref->next = c->xref_temp;
c->xref_temp = ref;
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...");
return -EIO;
}
......@@ -593,7 +767,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 */
}
......
......@@ -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,
struct jffs2_raw_summary *summary, uint32_t sumlen,
uint32_t *pseudo_random);
......@@ -177,6 +217,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 */
......
......@@ -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);
......
......@@ -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)
......
......@@ -37,7 +37,6 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
f->inocache->state = INO_STATE_PRESENT;
jffs2_add_ino_cache(c, f->inocache);
D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
ri->ino = cpu_to_je32(f->inocache->ino);
......
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2006 NEC Corporation
*
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this 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 "nodelist.h"
/* -------- xdatum related functions ----------------
* xattr_datum_hashkey(xprefix, xname, xvalue, xsize)
* is used to calcurate xdatum hashkey. The reminder of hashkey into XATTRINDEX_HASHSIZE is
* the index of the xattr name/value pair cache (c->xattrindex).
* unload_xattr_datum(c, xd)
* is used to release xattr name/value pair and detach from c->xattrindex.
* reclaim_xattr_datum(c)
* is used to reclaim xattr name/value pairs on the xattr name/value pair cache when
* memory usage by cache is over c->xdatum_mem_threshold. Currentry, this threshold
* is hard coded as 32KiB.
* delete_xattr_datum_node(c, xd)
* is used to delete a jffs2 node is dominated by xdatum. When EBS(Erase Block Summary) is
* enabled, it overwrites the obsolete node by myself.
* delete_xattr_datum(c, xd)
* is used to delete jffs2_xattr_datum object. It must be called with 0-value of reference
* counter. (It means how many jffs2_xattr_ref object refers this xdatum.)
* do_verify_xattr_datum(c, xd)
* is used to load the xdatum informations without name/value pair from the medium.
* It's necessary once, because those informations are not collected during mounting
* process when EBS is enabled.
* 0 will be returned, if success. An negative return value means recoverable error, and
* positive return value means unrecoverable error. Thus, caller must remove this xdatum
* and xref when it returned positive value.
* do_load_xattr_datum(c, xd)
* is used to load name/value pair from the medium.
* The meanings of return value is same as do_verify_xattr_datum().
* load_xattr_datum(c, xd)
* is used to be as a wrapper of do_verify_xattr_datum() and do_load_xattr_datum().
* If xd need to call do_verify_xattr_datum() at first, it's called before calling
* do_load_xattr_datum(). The meanings of return value is same as do_verify_xattr_datum().
* save_xattr_datum(c, xd, phys_ofs)
* is used to write xdatum to medium. xd->version will be incremented.
* create_xattr_datum(c, xprefix, xname, xvalue, xsize, phys_ofs)
* is used to create new xdatum and write to medium.
* -------------------------------------------------- */
static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize)
{
int name_len = strlen(xname);
return crc32(xprefix, xname, name_len) ^ crc32(xprefix, xvalue, xsize);
}
static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
D1(dbg_xattr("%s: xid=%u, version=%u\n", __FUNCTION__, xd->xid, xd->version));
if (xd->xname) {
c->xdatum_mem_usage -= (xd->name_len + 1 + xd->value_len);
kfree(xd->xname);
}
list_del_init(&xd->xindex);
xd->hashkey = 0;
xd->xname = NULL;
xd->xvalue = NULL;
}
static void reclaim_xattr_datum(struct jffs2_sb_info *c)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_xattr_datum *xd, *_xd;
uint32_t target, before;
static int index = 0;
int count;
if (c->xdatum_mem_threshold > c->xdatum_mem_usage)
return;
before = c->xdatum_mem_usage;
target = c->xdatum_mem_usage * 4 / 5; /* 20% reduction */
for (count = 0; count < XATTRINDEX_HASHSIZE; count++) {
list_for_each_entry_safe(xd, _xd, &c->xattrindex[index], xindex) {
if (xd->flags & JFFS2_XFLAGS_HOT) {
xd->flags &= ~JFFS2_XFLAGS_HOT;
} else if (!(xd->flags & JFFS2_XFLAGS_BIND)) {
unload_xattr_datum(c, xd);
}
if (c->xdatum_mem_usage <= target)
goto out;
}
index = (index+1) % XATTRINDEX_HASHSIZE;
}
out:
JFFS2_NOTICE("xdatum_mem_usage from %u byte to %u byte (%u byte reclaimed)\n",
before, c->xdatum_mem_usage, before - c->xdatum_mem_usage);
}
static void delete_xattr_datum_node(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_raw_xattr rx;
uint32_t length;
int rc;
if (!xd->node) {
JFFS2_WARNING("xdatum (xid=%u) is removed twice.\n", xd->xid);
return;
}
if (jffs2_sum_active()) {
memset(&rx, 0xff, sizeof(struct jffs2_raw_xattr));
rc = jffs2_flash_read(c, ref_offset(xd->node),
sizeof(struct jffs2_unknown_node),
&length, (char *)&rx);
if (rc || length != sizeof(struct jffs2_unknown_node)) {
JFFS2_ERROR("jffs2_flash_read()=%d, req=%u, read=%u at %#08x\n",
rc, sizeof(struct jffs2_unknown_node),
length, ref_offset(xd->node));
}
rc = jffs2_flash_write(c, ref_offset(xd->node), sizeof(rx),
&length, (char *)&rx);
if (rc || length != sizeof(struct jffs2_raw_xattr)) {
JFFS2_ERROR("jffs2_flash_write()=%d, req=%u, wrote=%u ar %#08x\n",
rc, sizeof(rx), length, ref_offset(xd->node));
}
}
spin_lock(&c->erase_completion_lock);
xd->node->next_in_ino = NULL;
spin_unlock(&c->erase_completion_lock);
jffs2_mark_node_obsolete(c, xd->node);
xd->node = NULL;
}
static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
BUG_ON(xd->refcnt);
unload_xattr_datum(c, xd);
if (xd->node) {
delete_xattr_datum_node(c, xd);
xd->node = NULL;
}
jffs2_free_xattr_datum(xd);
}
static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_eraseblock *jeb;
struct jffs2_raw_xattr rx;
size_t readlen;
uint32_t crc, totlen;
int rc;
BUG_ON(!xd->node);
BUG_ON(ref_flags(xd->node) != REF_UNCHECKED);
rc = jffs2_flash_read(c, ref_offset(xd->node), sizeof(rx), &readlen, (char *)&rx);
if (rc || readlen != sizeof(rx)) {
JFFS2_WARNING("jffs2_flash_read()=%d, req=%u, read=%u at %#08x\n",
rc, sizeof(rx), readlen, ref_offset(xd->node));
return rc ? rc : -EIO;
}
crc = crc32(0, &rx, sizeof(rx) - 4);
if (crc != je32_to_cpu(rx.node_crc)) {
if (je32_to_cpu(rx.node_crc) != 0xffffffff)
JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
ref_offset(xd->node), je32_to_cpu(rx.hdr_crc), crc);
return EIO;
}
totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len));
if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK
|| je16_to_cpu(rx.nodetype) != JFFS2_NODETYPE_XATTR
|| je32_to_cpu(rx.totlen) != totlen
|| je32_to_cpu(rx.xid) != xd->xid
|| je32_to_cpu(rx.version) != xd->version) {
JFFS2_ERROR("inconsistent xdatum at %#08x, magic=%#04x/%#04x, "
"nodetype=%#04x/%#04x, totlen=%u/%u, xid=%u/%u, version=%u/%u\n",
ref_offset(xd->node), je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK,
je16_to_cpu(rx.nodetype), JFFS2_NODETYPE_XATTR,
je32_to_cpu(rx.totlen), totlen,
je32_to_cpu(rx.xid), xd->xid,
je32_to_cpu(rx.version), xd->version);
return EIO;
}
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);
/* This JFFS2_NODETYPE_XATTR node is checked */
jeb = &c->blocks[ref_offset(xd->node) / c->sector_size];
totlen = PAD(je32_to_cpu(rx.totlen));
spin_lock(&c->erase_completion_lock);
c->unchecked_size -= totlen; c->used_size += totlen;
jeb->unchecked_size -= totlen; jeb->used_size += totlen;
xd->node->flash_offset = ref_offset(xd->node) | REF_PRISTINE;
spin_unlock(&c->erase_completion_lock);
/* unchecked xdatum is chained with c->xattr_unchecked */
list_del_init(&xd->xindex);
dbg_xattr("success on verfying xdatum (xid=%u, version=%u)\n",
xd->xid, xd->version);
return 0;
}
static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem) */
char *data;
size_t readlen;
uint32_t crc, length;
int i, ret, retry = 0;
BUG_ON(!xd->node);
BUG_ON(ref_flags(xd->node) != REF_PRISTINE);
BUG_ON(!list_empty(&xd->xindex));
retry:
length = xd->name_len + 1 + xd->value_len;
data = kmalloc(length, GFP_KERNEL);
if (!data)
return -ENOMEM;
ret = jffs2_flash_read(c, ref_offset(xd->node)+sizeof(struct jffs2_raw_xattr),
length, &readlen, data);
if (ret || length!=readlen) {
JFFS2_WARNING("jffs2_flash_read() returned %d, request=%d, readlen=%d, at %#08x\n",
ret, length, readlen, ref_offset(xd->node));
kfree(data);
return ret ? ret : -EIO;
}
data[xd->name_len] = '\0';
crc = crc32(0, data, length);
if (crc != xd->data_crc) {
JFFS2_WARNING("node CRC failed (JFFS2_NODETYPE_XREF)"
" at %#08x, read: 0x%08x calculated: 0x%08x\n",
ref_offset(xd->node), xd->data_crc, crc);
kfree(data);
return EIO;
}
xd->flags |= JFFS2_XFLAGS_HOT;
xd->xname = data;
xd->xvalue = data + xd->name_len+1;
c->xdatum_mem_usage += length;
xd->hashkey = xattr_datum_hashkey(xd->xprefix, xd->xname, xd->xvalue, xd->value_len);
i = xd->hashkey % XATTRINDEX_HASHSIZE;
list_add(&xd->xindex, &c->xattrindex[i]);
if (!retry) {
retry = 1;
reclaim_xattr_datum(c);
if (!xd->xname)
goto retry;
}
dbg_xattr("success on loading xdatum (xid=%u, xprefix=%u, xname='%s')\n",
xd->xid, xd->xprefix, xd->xname);
return 0;
}
static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
/* must be called under down_write(xattr_sem);
* rc < 0 : recoverable error, try again
* rc = 0 : success
* rc > 0 : Unrecoverable error, this node should be deleted.
*/
int rc = 0;
BUG_ON(xd->xname);
if (!xd->node)
return EIO;
if (unlikely(ref_flags(xd->node) != REF_PRISTINE)) {
rc = do_verify_xattr_datum(c, xd);
if (rc > 0) {
list_del_init(&xd->xindex);
delete_xattr_datum_node(c, xd);
}
}
if (!rc)
rc = do_load_xattr_datum(c, xd);
return rc;
}
static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd, uint32_t phys_ofs)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_raw_xattr rx;
struct jffs2_raw_node_ref *raw;
struct kvec vecs[2];
uint32_t length;
int rc, totlen;
BUG_ON(!xd->xname);
vecs[0].iov_base = &rx;
vecs[0].iov_len = PAD(sizeof(rx));
vecs[1].iov_base = xd->xname;
vecs[1].iov_len = xd->name_len + 1 + xd->value_len;
totlen = vecs[0].iov_len + vecs[1].iov_len;
raw = jffs2_alloc_raw_node_ref();
if (!raw)
return -ENOMEM;
raw->flash_offset = phys_ofs;
raw->__totlen = PAD(totlen);
raw->next_phys = NULL;
raw->next_in_ino = (void *)xd;
/* Setup raw-xattr */
rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR);
rx.totlen = cpu_to_je32(PAD(totlen));
rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4));
rx.xid = cpu_to_je32(xd->xid);
rx.version = cpu_to_je32(++xd->version);
rx.xprefix = xd->xprefix;
rx.name_len = xd->name_len;
rx.value_len = cpu_to_je16(xd->value_len);
rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len));
rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_raw_xattr) - 4));
rc = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0);
if (rc || totlen != length) {
JFFS2_WARNING("jffs2_flash_writev()=%d, req=%u, wrote=%u, at %#08x\n",
rc, totlen, length, phys_ofs);
rc = rc ? rc : -EIO;
if (length) {
raw->flash_offset |= REF_OBSOLETE;
raw->next_in_ino = NULL;
jffs2_add_physical_node_ref(c, raw);
jffs2_mark_node_obsolete(c, raw);
} else {
jffs2_free_raw_node_ref(raw);
}
return rc;
}
BUG_ON(raw->__totlen < sizeof(struct jffs2_raw_xattr));
/* success */
raw->flash_offset |= REF_PRISTINE;
jffs2_add_physical_node_ref(c, raw);
if (xd->node)
delete_xattr_datum_node(c, xd);
xd->node = raw;
dbg_xattr("success on saving xdatum (xid=%u, version=%u, xprefix=%u, xname='%s')\n",
xd->xid, xd->version, xd->xprefix, xd->xname);
return 0;
}
static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c,
int xprefix, const char *xname,
const char *xvalue, int xsize,
uint32_t phys_ofs)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_xattr_datum *xd;
uint32_t hashkey, name_len;
char *data;
int i, rc;
/* Search xattr_datum has same xname/xvalue by index */
hashkey = xattr_datum_hashkey(xprefix, xname, xvalue, xsize);
i = hashkey % XATTRINDEX_HASHSIZE;
list_for_each_entry(xd, &c->xattrindex[i], xindex) {
if (xd->hashkey==hashkey
&& xd->xprefix==xprefix
&& xd->value_len==xsize
&& !strcmp(xd->xname, xname)
&& !memcmp(xd->xvalue, xvalue, xsize)) {
xd->refcnt++;
return xd;
}
}
/* Not found, Create NEW XATTR-Cache */
name_len = strlen(xname);
xd = jffs2_alloc_xattr_datum();
if (!xd)
return ERR_PTR(-ENOMEM);
data = kmalloc(name_len + 1 + xsize, GFP_KERNEL);
if (!data) {
jffs2_free_xattr_datum(xd);
return ERR_PTR(-ENOMEM);
}
strcpy(data, xname);
memcpy(data + name_len + 1, xvalue, xsize);
xd->refcnt = 1;
xd->xid = ++c->highest_xid;
xd->flags |= JFFS2_XFLAGS_HOT;
xd->xprefix = xprefix;
xd->hashkey = hashkey;
xd->xname = data;
xd->xvalue = data + name_len + 1;
xd->name_len = name_len;
xd->value_len = xsize;
xd->data_crc = crc32(0, data, xd->name_len + 1 + xd->value_len);
rc = save_xattr_datum(c, xd, phys_ofs);
if (rc) {
kfree(xd->xname);
jffs2_free_xattr_datum(xd);
return ERR_PTR(rc);
}
/* Insert Hash Index */
i = hashkey % XATTRINDEX_HASHSIZE;
list_add(&xd->xindex, &c->xattrindex[i]);
c->xdatum_mem_usage += (xd->name_len + 1 + xd->value_len);
reclaim_xattr_datum(c);
return xd;
}
/* -------- xref related functions ------------------
* verify_xattr_ref(c, ref)
* is used to load xref information from medium. Because summary data does not
* contain xid/ino, it's necessary to verify once while mounting process.
* delete_xattr_ref_node(c, ref)
* is used to delete a jffs2 node is dominated by xref. When EBS is enabled,
* it overwrites the obsolete node by myself.
* delete_xattr_ref(c, ref)
* is used to delete jffs2_xattr_ref object. If the reference counter of xdatum
* is refered by this xref become 0, delete_xattr_datum() is called later.
* save_xattr_ref(c, ref, phys_ofs)
* is used to write xref to medium.
* create_xattr_ref(c, ic, xd, phys_ofs)
* is used to create a new xref and write to medium.
* jffs2_xattr_delete_inode(c, ic)
* is called to remove xrefs related to obsolete inode when inode is unlinked.
* jffs2_xattr_free_inode(c, ic)
* is called to release xattr related objects when unmounting.
* check_xattr_ref_inode(c, ic)
* is used to confirm inode does not have duplicate xattr name/value pair.
* -------------------------------------------------- */
static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
{
struct jffs2_eraseblock *jeb;
struct jffs2_raw_xref rr;
size_t readlen;
uint32_t crc, totlen;
int rc;
BUG_ON(ref_flags(ref->node) != REF_UNCHECKED);
rc = jffs2_flash_read(c, ref_offset(ref->node), sizeof(rr), &readlen, (char *)&rr);
if (rc || sizeof(rr) != readlen) {
JFFS2_WARNING("jffs2_flash_read()=%d, req=%u, read=%u, at %#08x\n",
rc, sizeof(rr), readlen, ref_offset(ref->node));
return rc ? rc : -EIO;
}
/* obsolete node */
crc = crc32(0, &rr, sizeof(rr) - 4);
if (crc != je32_to_cpu(rr.node_crc)) {
if (je32_to_cpu(rr.node_crc) != 0xffffffff)
JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n",
ref_offset(ref->node), je32_to_cpu(rr.node_crc), crc);
return EIO;
}
if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK
|| je16_to_cpu(rr.nodetype) != JFFS2_NODETYPE_XREF
|| je32_to_cpu(rr.totlen) != PAD(sizeof(rr))) {
JFFS2_ERROR("inconsistent xref at %#08x, magic=%#04x/%#04x, "
"nodetype=%#04x/%#04x, totlen=%u/%u\n",
ref_offset(ref->node), je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK,
je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF,
je32_to_cpu(rr.totlen), PAD(sizeof(rr)));
return EIO;
}
ref->ino = je32_to_cpu(rr.ino);
ref->xid = je32_to_cpu(rr.xid);
/* fixup superblock/eraseblock info */
jeb = &c->blocks[ref_offset(ref->node) / c->sector_size];
totlen = PAD(sizeof(rr));
spin_lock(&c->erase_completion_lock);
c->unchecked_size -= totlen; c->used_size += totlen;
jeb->unchecked_size -= totlen; jeb->used_size += totlen;
ref->node->flash_offset = ref_offset(ref->node) | REF_PRISTINE;
spin_unlock(&c->erase_completion_lock);
dbg_xattr("success on verifying xref (ino=%u, xid=%u) at %#08x\n",
ref->ino, ref->xid, ref_offset(ref->node));
return 0;
}
static void delete_xattr_ref_node(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
{
struct jffs2_raw_xref rr;
uint32_t length;
int rc;
if (jffs2_sum_active()) {
memset(&rr, 0xff, sizeof(rr));
rc = jffs2_flash_read(c, ref_offset(ref->node),
sizeof(struct jffs2_unknown_node),
&length, (char *)&rr);
if (rc || length != sizeof(struct jffs2_unknown_node)) {
JFFS2_ERROR("jffs2_flash_read()=%d, req=%u, read=%u at %#08x\n",
rc, sizeof(struct jffs2_unknown_node),
length, ref_offset(ref->node));
}
rc = jffs2_flash_write(c, ref_offset(ref->node), sizeof(rr),
&length, (char *)&rr);
if (rc || length != sizeof(struct jffs2_raw_xref)) {
JFFS2_ERROR("jffs2_flash_write()=%d, req=%u, wrote=%u at %#08x\n",
rc, sizeof(rr), length, ref_offset(ref->node));
}
}
spin_lock(&c->erase_completion_lock);
ref->node->next_in_ino = NULL;
spin_unlock(&c->erase_completion_lock);
jffs2_mark_node_obsolete(c, ref->node);
ref->node = NULL;
}
static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_xattr_datum *xd;
BUG_ON(!ref->node);
delete_xattr_ref_node(c, ref);
xd = ref->xd;
xd->refcnt--;
if (!xd->refcnt)
delete_xattr_datum(c, xd);
jffs2_free_xattr_ref(ref);
}
static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, uint32_t phys_ofs)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_raw_node_ref *raw;
struct jffs2_raw_xref rr;
uint32_t length;
int ret;
raw = jffs2_alloc_raw_node_ref();
if (!raw)
return -ENOMEM;
raw->flash_offset = phys_ofs;
raw->__totlen = PAD(sizeof(rr));
raw->next_phys = NULL;
raw->next_in_ino = (void *)ref;
rr.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
rr.nodetype = cpu_to_je16(JFFS2_NODETYPE_XREF);
rr.totlen = cpu_to_je32(PAD(sizeof(rr)));
rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node) - 4));
rr.ino = cpu_to_je32(ref->ic->ino);
rr.xid = cpu_to_je32(ref->xd->xid);
rr.node_crc = cpu_to_je32(crc32(0, &rr, sizeof(rr) - 4));
ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr);
if (ret || sizeof(rr) != length) {
JFFS2_WARNING("jffs2_flash_write() returned %d, request=%u, retlen=%u, at %#08x\n",
ret, sizeof(rr), length, phys_ofs);
ret = ret ? ret : -EIO;
if (length) {
raw->flash_offset |= REF_OBSOLETE;
raw->next_in_ino = NULL;
jffs2_add_physical_node_ref(c, raw);
jffs2_mark_node_obsolete(c, raw);
} else {
jffs2_free_raw_node_ref(raw);
}
return ret;
}
raw->flash_offset |= REF_PRISTINE;
jffs2_add_physical_node_ref(c, raw);
if (ref->node)
delete_xattr_ref_node(c, ref);
ref->node = raw;
dbg_xattr("success on saving xref (ino=%u, xid=%u)\n", ref->ic->ino, ref->xd->xid);
return 0;
}
static struct jffs2_xattr_ref *create_xattr_ref(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic,
struct jffs2_xattr_datum *xd, uint32_t phys_ofs)
{
/* must be called under down_write(xattr_sem) */
struct jffs2_xattr_ref *ref;
int ret;
ref = jffs2_alloc_xattr_ref();
if (!ref)
return ERR_PTR(-ENOMEM);
ref->ic = ic;
ref->xd = xd;
ret = save_xattr_ref(c, ref, phys_ofs);
if (ret) {
jffs2_free_xattr_ref(ref);
return ERR_PTR(ret);
}
/* Chain to inode */
ref->next = ic->xref;
ic->xref = ref;
return ref; /* success */
}
void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{
/* It's called from jffs2_clear_inode() on inode removing.
When an inode with XATTR is removed, those XATTRs must be removed. */
struct jffs2_xattr_ref *ref, *_ref;
if (!ic || ic->nlink > 0)
return;
down_write(&c->xattr_sem);
for (ref = ic->xref; ref; ref = _ref) {
_ref = ref->next;
delete_xattr_ref(c, ref);
}
ic->xref = NULL;
up_write(&c->xattr_sem);
}
void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{
/* It's called from jffs2_free_ino_caches() until unmounting FS. */
struct jffs2_xattr_datum *xd;
struct jffs2_xattr_ref *ref, *_ref;
down_write(&c->xattr_sem);
for (ref = ic->xref; ref; ref = _ref) {
_ref = ref->next;
xd = ref->xd;
xd->refcnt--;
if (!xd->refcnt) {
unload_xattr_datum(c, xd);
jffs2_free_xattr_datum(xd);
}
jffs2_free_xattr_ref(ref);
}
ic->xref = NULL;
up_write(&c->xattr_sem);
}
static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
{
/* success of check_xattr_ref_inode() means taht inode (ic) dose not have
* duplicate name/value pairs. If duplicate name/value pair would be found,
* one will be removed.
*/
struct jffs2_xattr_ref *ref, *cmp, **pref;
int rc = 0;
if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED))
return 0;
down_write(&c->xattr_sem);
retry:
rc = 0;
for (ref=ic->xref, pref=&ic->xref; ref; pref=&ref->next, ref=ref->next) {
if (!ref->xd->xname) {
rc = load_xattr_datum(c, ref->xd);
if (unlikely(rc > 0)) {
*pref = ref->next;
delete_xattr_ref(c, ref);
goto retry;
} else if (unlikely(rc < 0))
goto out;
}
for (cmp=ref->next, pref=&ref->next; cmp; pref=&cmp->next, cmp=cmp->next) {
if (!cmp->xd->xname) {
ref->xd->flags |= JFFS2_XFLAGS_BIND;
rc = load_xattr_datum(c, cmp->xd);
ref->xd->flags &= ~JFFS2_XFLAGS_BIND;
if (unlikely(rc > 0)) {
*pref = cmp->next;
delete_xattr_ref(c, cmp);
goto retry;
} else if (unlikely(rc < 0))
goto out;
}
if (ref->xd->xprefix == cmp->xd->xprefix
&& !strcmp(ref->xd->xname, cmp->xd->xname)) {
*pref = cmp->next;
delete_xattr_ref(c, cmp);
goto retry;
}
}
}
ic->flags |= INO_FLAGS_XATTR_CHECKED;
out:
up_write(&c->xattr_sem);
return rc;
}
/* -------- xattr subsystem functions ---------------
* jffs2_init_xattr_subsystem(c)
* is used to initialize semaphore and list_head, and some variables.
* jffs2_find_xattr_datum(c, xid)
* is used to lookup xdatum while scanning process.
* jffs2_clear_xattr_subsystem(c)
* is used to release any xattr related objects.
* jffs2_build_xattr_subsystem(c)
* is used to associate xdatum and xref while super block building process.
* jffs2_setup_xattr_datum(c, xid, version)
* is used to insert xdatum while scanning process.
* -------------------------------------------------- */
void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c)
{
int i;
for (i=0; i < XATTRINDEX_HASHSIZE; i++)
INIT_LIST_HEAD(&c->xattrindex[i]);
INIT_LIST_HEAD(&c->xattr_unchecked);
c->xref_temp = NULL;
init_rwsem(&c->xattr_sem);
c->xdatum_mem_usage = 0;
c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */
}
static struct jffs2_xattr_datum *jffs2_find_xattr_datum(struct jffs2_sb_info *c, uint32_t xid)
{
struct jffs2_xattr_datum *xd;
int i = xid % XATTRINDEX_HASHSIZE;
/* It's only used in scanning/building process. */
BUG_ON(!(c->flags & (JFFS2_SB_FLAG_SCANNING|JFFS2_SB_FLAG_BUILDING)));
list_for_each_entry(xd, &c->xattrindex[i], xindex) {
if (xd->xid==xid)
return xd;
}
return NULL;
}
void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c)
{
struct jffs2_xattr_datum *xd, *_xd;
struct jffs2_xattr_ref *ref, *_ref;
int i;
for (ref=c->xref_temp; ref; ref = _ref) {
_ref = ref->next;
jffs2_free_xattr_ref(ref);
}
c->xref_temp = NULL;
for (i=0; i < XATTRINDEX_HASHSIZE; i++) {
list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) {
list_del(&xd->xindex);
if (xd->xname)
kfree(xd->xname);
jffs2_free_xattr_datum(xd);
}
}
}
void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c)
{
struct jffs2_xattr_ref *ref, *_ref;
struct jffs2_xattr_datum *xd, *_xd;
struct jffs2_inode_cache *ic;
int i, xdatum_count =0, xdatum_unchecked_count = 0, xref_count = 0;
BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING));
/* Phase.1 */
for (ref=c->xref_temp; ref; ref=_ref) {
_ref = ref->next;
/* checking REF_UNCHECKED nodes */
if (ref_flags(ref->node) != REF_PRISTINE) {
if (verify_xattr_ref(c, ref)) {
delete_xattr_ref_node(c, ref);
jffs2_free_xattr_ref(ref);
continue;
}
}
/* At this point, ref->xid and ref->ino contain XID and inode number.
ref->xd and ref->ic are not valid yet. */
xd = jffs2_find_xattr_datum(c, ref->xid);
ic = jffs2_get_ino_cache(c, ref->ino);
if (!xd || !ic) {
if (ref_flags(ref->node) != REF_UNCHECKED)
JFFS2_WARNING("xref(ino=%u, xid=%u) is orphan. \n",
ref->ino, ref->xid);
delete_xattr_ref_node(c, ref);
jffs2_free_xattr_ref(ref);
continue;
}
ref->xd = xd;
ref->ic = ic;
xd->refcnt++;
ref->next = ic->xref;
ic->xref = ref;
xref_count++;
}
c->xref_temp = NULL;
/* After this, ref->xid/ino are NEVER used. */
/* Phase.2 */
for (i=0; i < XATTRINDEX_HASHSIZE; i++) {
list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) {
list_del_init(&xd->xindex);
if (!xd->refcnt) {
if (ref_flags(xd->node) != REF_UNCHECKED)
JFFS2_WARNING("orphan xdatum(xid=%u, version=%u) at %#08x\n",
xd->xid, xd->version, ref_offset(xd->node));
delete_xattr_datum(c, xd);
continue;
}
if (ref_flags(xd->node) != REF_PRISTINE) {
dbg_xattr("unchecked xdatum(xid=%u) at %#08x\n",
xd->xid, ref_offset(xd->node));
list_add(&xd->xindex, &c->xattr_unchecked);
xdatum_unchecked_count++;
}
xdatum_count++;
}
}
/* build complete */
JFFS2_NOTICE("complete building xattr subsystem, %u of xdatum (%u unchecked) and "
"%u of xref found.\n", xdatum_count, xdatum_unchecked_count, xref_count);
}
struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c,
uint32_t xid, uint32_t version)
{
struct jffs2_xattr_datum *xd, *_xd;
_xd = jffs2_find_xattr_datum(c, xid);
if (_xd) {
dbg_xattr("duplicate xdatum (xid=%u, version=%u/%u) at %#08x\n",
xid, version, _xd->version, ref_offset(_xd->node));
if (version < _xd->version)
return ERR_PTR(-EEXIST);
}
xd = jffs2_alloc_xattr_datum();
if (!xd)
return ERR_PTR(-ENOMEM);
xd->xid = xid;
xd->version = version;
if (xd->xid > c->highest_xid)
c->highest_xid = xd->xid;
list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]);
if (_xd) {
list_del_init(&_xd->xindex);
delete_xattr_datum_node(c, _xd);
jffs2_free_xattr_datum(_xd);
}
return xd;
}
/* -------- xattr subsystem functions ---------------
* xprefix_to_handler(xprefix)
* is used to translate xprefix into xattr_handler.
* jffs2_listxattr(dentry, buffer, size)
* is an implementation of listxattr handler on jffs2.
* do_jffs2_getxattr(inode, xprefix, xname, buffer, size)
* is an implementation of getxattr handler on jffs2.
* do_jffs2_setxattr(inode, xprefix, xname, buffer, size, flags)
* is an implementation of setxattr handler on jffs2.
* -------------------------------------------------- */
struct xattr_handler *jffs2_xattr_handlers[] = {
&jffs2_user_xattr_handler,
#ifdef CONFIG_JFFS2_FS_SECURITY
&jffs2_security_xattr_handler,
#endif
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
&jffs2_acl_access_xattr_handler,
&jffs2_acl_default_xattr_handler,
#endif
&jffs2_trusted_xattr_handler,
NULL
};
static struct xattr_handler *xprefix_to_handler(int xprefix) {
struct xattr_handler *ret;
switch (xprefix) {
case JFFS2_XPREFIX_USER:
ret = &jffs2_user_xattr_handler;
break;
#ifdef CONFIG_JFFS2_FS_SECURITY
case JFFS2_XPREFIX_SECURITY:
ret = &jffs2_security_xattr_handler;
break;
#endif
#ifdef CONFIG_JFFS2_FS_POSIX_ACL
case JFFS2_XPREFIX_ACL_ACCESS:
ret = &jffs2_acl_access_xattr_handler;
break;
case JFFS2_XPREFIX_ACL_DEFAULT:
ret = &jffs2_acl_default_xattr_handler;
break;
#endif
case JFFS2_XPREFIX_TRUSTED:
ret = &jffs2_trusted_xattr_handler;
break;
default:
ret = NULL;
break;
}
return ret;
}
ssize_t jffs2_listxattr(struct dentry *dentry, char *buffer, size_t size)
{
struct inode *inode = dentry->d_inode;
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_inode_cache *ic = f->inocache;
struct jffs2_xattr_ref *ref, **pref;
struct jffs2_xattr_datum *xd;
struct xattr_handler *xhandle;
ssize_t len, rc;
int retry = 0;
rc = check_xattr_ref_inode(c, ic);
if (unlikely(rc))
return rc;
down_read(&c->xattr_sem);
retry:
len = 0;
for (ref=ic->xref, pref=&ic->xref; ref; pref=&ref->next, ref=ref->next) {
BUG_ON(ref->ic != ic);
xd = ref->xd;
if (!xd->xname) {
/* xdatum is unchached */
if (!retry) {
retry = 1;
up_read(&c->xattr_sem);
down_write(&c->xattr_sem);
goto retry;
} else {
rc = load_xattr_datum(c, xd);
if (unlikely(rc > 0)) {
*pref = ref->next;
delete_xattr_ref(c, ref);
goto retry;
} else if (unlikely(rc < 0))
goto out;
}
}
xhandle = xprefix_to_handler(xd->xprefix);
if (!xhandle)
continue;
if (buffer) {
rc = xhandle->list(inode, buffer+len, size-len, xd->xname, xd->name_len);
} else {
rc = xhandle->list(inode, NULL, 0, xd->xname, xd->name_len);
}
if (rc < 0)
goto out;
len += rc;
}
rc = len;
out:
if (!retry) {
up_read(&c->xattr_sem);
} else {
up_write(&c->xattr_sem);
}
return rc;
}
int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname,
char *buffer, size_t size)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_inode_cache *ic = f->inocache;
struct jffs2_xattr_datum *xd;
struct jffs2_xattr_ref *ref, **pref;
int rc, retry = 0;
rc = check_xattr_ref_inode(c, ic);
if (unlikely(rc))
return rc;
down_read(&c->xattr_sem);
retry:
for (ref=ic->xref, pref=&ic->xref; ref; pref=&ref->next, ref=ref->next) {
BUG_ON(ref->ic!=ic);
xd = ref->xd;
if (xd->xprefix != xprefix)
continue;
if (!xd->xname) {
/* xdatum is unchached */
if (!retry) {
retry = 1;
up_read(&c->xattr_sem);
down_write(&c->xattr_sem);
goto retry;
} else {
rc = load_xattr_datum(c, xd);
if (unlikely(rc > 0)) {
*pref = ref->next;
delete_xattr_ref(c, ref);
goto retry;
} else if (unlikely(rc < 0)) {
goto out;
}
}
}
if (!strcmp(xname, xd->xname)) {
rc = xd->value_len;
if (buffer) {
if (size < rc) {
rc = -ERANGE;
} else {
memcpy(buffer, xd->xvalue, rc);
}
}
goto out;
}
}
rc = -ENODATA;
out:
if (!retry) {
up_read(&c->xattr_sem);
} else {
up_write(&c->xattr_sem);
}
return rc;
}
int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
const char *buffer, size_t size, int flags)
{
struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
struct jffs2_inode_cache *ic = f->inocache;
struct jffs2_xattr_datum *xd;
struct jffs2_xattr_ref *ref, *newref, **pref;
uint32_t phys_ofs, length, request;
int rc;
rc = check_xattr_ref_inode(c, ic);
if (unlikely(rc))
return rc;
request = PAD(sizeof(struct jffs2_raw_xattr) + strlen(xname) + 1 + size);
rc = jffs2_reserve_space(c, request, &phys_ofs, &length,
ALLOC_NORMAL, JFFS2_SUMMARY_XATTR_SIZE);
if (rc) {
JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request);
return rc;
}
/* Find existing xattr */
down_write(&c->xattr_sem);
retry:
for (ref=ic->xref, pref=&ic->xref; ref; pref=&ref->next, ref=ref->next) {
xd = ref->xd;
if (xd->xprefix != xprefix)
continue;
if (!xd->xname) {
rc = load_xattr_datum(c, xd);
if (unlikely(rc > 0)) {
*pref = ref->next;
delete_xattr_ref(c, ref);
goto retry;
} else if (unlikely(rc < 0))
goto out;
}
if (!strcmp(xd->xname, xname)) {
if (flags & XATTR_CREATE) {
rc = -EEXIST;
goto out;
}
if (!buffer) {
*pref = ref->next;
delete_xattr_ref(c, ref);
rc = 0;
goto out;
}
goto found;
}
}
/* not found */
if (flags & XATTR_REPLACE) {
rc = -ENODATA;
goto out;
}
if (!buffer) {
rc = -EINVAL;
goto out;
}
found:
xd = create_xattr_datum(c, xprefix, xname, buffer, size, phys_ofs);
if (IS_ERR(xd)) {
rc = PTR_ERR(xd);
goto out;
}
up_write(&c->xattr_sem);
jffs2_complete_reservation(c);
/* create xattr_ref */
request = PAD(sizeof(struct jffs2_raw_xref));
rc = jffs2_reserve_space(c, request, &phys_ofs, &length,
ALLOC_NORMAL, JFFS2_SUMMARY_XREF_SIZE);
if (rc) {
JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request);
down_write(&c->xattr_sem);
xd->refcnt--;
if (!xd->refcnt)
delete_xattr_datum(c, xd);
up_write(&c->xattr_sem);
return rc;
}
down_write(&c->xattr_sem);
if (ref)
*pref = ref->next;
newref = create_xattr_ref(c, ic, xd, phys_ofs);
if (IS_ERR(newref)) {
if (ref) {
ref->next = ic->xref;
ic->xref = ref;
}
rc = PTR_ERR(newref);
xd->refcnt--;
if (!xd->refcnt)
delete_xattr_datum(c, xd);
} else if (ref) {
delete_xattr_ref(c, ref);
}
out:
up_write(&c->xattr_sem);
jffs2_complete_reservation(c);
return rc;
}
/* -------- garbage collector functions -------------
* jffs2_garbage_collect_xattr_datum(c, xd)
* is used to move xdatum into new node.
* jffs2_garbage_collect_xattr_ref(c, ref)
* is used to move xref into new node.
* jffs2_verify_xattr(c)
* is used to call do_verify_xattr_datum() before garbage collecting.
* -------------------------------------------------- */
int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
{
uint32_t phys_ofs, totlen, length, old_ofs;
int rc = -EINVAL;
down_write(&c->xattr_sem);
BUG_ON(!xd->node);
old_ofs = ref_offset(xd->node);
totlen = ref_totlen(c, c->gcblock, xd->node);
if (totlen < sizeof(struct jffs2_raw_xattr))
goto out;
if (!xd->xname) {
rc = load_xattr_datum(c, xd);
if (unlikely(rc > 0)) {
delete_xattr_datum_node(c, xd);
rc = 0;
goto out;
} else if (unlikely(rc < 0))
goto out;
}
rc = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_XATTR_SIZE);
if (rc || length < totlen) {
JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, totlen);
rc = rc ? rc : -EBADFD;
goto out;
}
rc = save_xattr_datum(c, xd, phys_ofs);
if (!rc)
dbg_xattr("xdatum (xid=%u, version=%u) GC'ed from %#08x to %08x\n",
xd->xid, xd->version, old_ofs, ref_offset(xd->node));
out:
up_write(&c->xattr_sem);
return rc;
}
int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref)
{
uint32_t phys_ofs, totlen, length, old_ofs;
int rc = -EINVAL;
down_write(&c->xattr_sem);
BUG_ON(!ref->node);
old_ofs = ref_offset(ref->node);
totlen = ref_totlen(c, c->gcblock, ref->node);
if (totlen != sizeof(struct jffs2_raw_xref))
goto out;
rc = jffs2_reserve_space_gc(c, totlen, &phys_ofs, &length, JFFS2_SUMMARY_XREF_SIZE);
if (rc || length < totlen) {
JFFS2_WARNING("%s: jffs2_reserve_space() = %d, request = %u\n",
__FUNCTION__, rc, totlen);
rc = rc ? rc : -EBADFD;
goto out;
}
rc = save_xattr_ref(c, ref, phys_ofs);
if (!rc)
dbg_xattr("xref (ino=%u, xid=%u) GC'ed from %#08x to %08x\n",
ref->ic->ino, ref->xd->xid, old_ofs, ref_offset(ref->node));
out:
up_write(&c->xattr_sem);
return rc;
}
int jffs2_verify_xattr(struct jffs2_sb_info *c)
{
struct jffs2_xattr_datum *xd, *_xd;
int rc;
down_write(&c->xattr_sem);
list_for_each_entry_safe(xd, _xd, &c->xattr_unchecked, xindex) {
rc = do_verify_xattr_datum(c, xd);
if (rc == 0) {
list_del_init(&xd->xindex);
break;
} else if (rc > 0) {
list_del_init(&xd->xindex);
delete_xattr_datum_node(c, xd);
}
}
up_write(&c->xattr_sem);
return list_empty(&c->xattr_unchecked) ? 1 : 0;
}
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2006 NEC Corporation
*
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this directory.
*
*/
#ifndef _JFFS2_FS_XATTR_H_
#define _JFFS2_FS_XATTR_H_
#include <linux/xattr.h>
#include <linux/list.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;
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 jffs2_xattr_ref *next; /* chained from ic->xref_list */
};
#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_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd);
extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref);
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
#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_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
#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_ */
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2006 NEC Corporation
*
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this 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
};
/*
* JFFS2 -- Journalling Flash File System, Version 2.
*
* Copyright (C) 2006 NEC Corporation
*
* Created by KaiGai Kohei <kaigai@ak.jp.nec.com>
*
* For licensing information, see the file 'LICENCE' in this 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
};
......@@ -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];
};
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;
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment