diff -uNr linux-2.6.10.mxc/fs/jffs2/file.c linux-2.6.10.jffs2/fs/jffs2/file.c --- linux-2.6.10.mxc/fs/jffs2/file.c 2005-08-19 15:40:26.000000000 +0800 +++ linux-2.6.10.jffs2/fs/jffs2/file.c 2005-08-19 15:48:40.000000000 +0800 @@ -20,6 +20,9 @@ #include #include #include +#ifdef CONFIG_JFFS2_FS_XATTR +#include +#endif #include "nodelist.h" extern int generic_file_open(struct inode *, struct file *) __attribute__((weak)); @@ -55,6 +58,12 @@ struct inode_operations jffs2_file_inode_operations = { +#ifdef CONFIG_JFFS2_FS_XATTR + .setxattr = jffs2_setxattr, + .getxattr = jffs2_getxattr, + .listxattr = jffs2_listxattr, + .removexattr = jffs2_removexattr, +#endif .setattr = jffs2_setattr }; diff -uNr linux-2.6.10.mxc/fs/jffs2/Makefile linux-2.6.10.jffs2/fs/jffs2/Makefile --- linux-2.6.10.mxc/fs/jffs2/Makefile 2005-08-19 15:40:26.000000000 +0800 +++ linux-2.6.10.jffs2/fs/jffs2/Makefile 2005-08-19 15:44:39.000000000 +0800 @@ -11,6 +11,7 @@ jffs2-y += symlink.o build.o erase.o background.o fs.o writev.o jffs2-y += super.o +jffs2-$(CONFIG_JFFS2_FS_XATTR) += xattr.o jffs2-$(CONFIG_JFFS2_FS_NAND) += wbuf.o jffs2-$(CONFIG_JFFS2_FS_NOR_ECC) += wbuf.o jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o diff -uNr linux-2.6.10.mxc/fs/jffs2/nodelist.c linux-2.6.10.jffs2/fs/jffs2/nodelist.c --- linux-2.6.10.mxc/fs/jffs2/nodelist.c 2005-08-19 15:40:26.000000000 +0800 +++ linux-2.6.10.jffs2/fs/jffs2/nodelist.c 2005-08-19 15:47:12.000000000 +0800 @@ -173,6 +173,7 @@ goto free_out; } /* sanity check */ + #ifndef CONFIG_JFFS2_FS_XATTR if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) { printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n", ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen)); @@ -180,6 +181,7 @@ spin_lock(&c->erase_completion_lock); continue; } + #endif if (je32_to_cpu(node.d.version) > *highest_version) *highest_version = je32_to_cpu(node.d.version); if (ref_obsolete(ref)) { diff -uNr linux-2.6.10.mxc/fs/jffs2/xattr.c linux-2.6.10.jffs2/fs/jffs2/xattr.c --- linux-2.6.10.mxc/fs/jffs2/xattr.c 1970-01-01 08:00:00.000000000 +0800 +++ linux-2.6.10.jffs2/fs/jffs2/xattr.c 2005-08-19 16:17:29.000000000 +0800 @@ -0,0 +1,467 @@ +/* + * + * Copyright 2005 Motorola, Inc. All Rights Reserved. + * + * Created by Ma yun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nodelist.h" + + +struct xattr_buffer { + int size; /* the size of current use buffer */ + struct jffs2_xattr_list *xattr; /* buffer containing EAs list */ +}; + +static int ea_get(struct dentry *parent, struct inode *inode, struct xattr_buffer *ea_buf, int min_size) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode), *p; + struct jffs2_raw_dirent findnode; + struct jffs2_raw_node_ref *raw = NULL; + struct jffs2_full_dirent *fd_list = NULL; + size_t readlen; + int size; + uint32_t offset = 0; + uint32_t ino = f->inocache->ino; + int mem_size = 0; + int ret = 0; + int n_version =0 ; + + memset(&findnode, 0, sizeof(findnode)); + /* lookup the dirent list and find raw dirent*/ + + p = JFFS2_INODE_INFO(parent->d_inode); + down(&p->sem); + for(fd_list = p->dents; fd_list; fd_list = fd_list->next){ + if (fd_list->ino == ino && fd_list->version >= n_version) n_version = fd_list->version; + } + + for(fd_list = p->dents; fd_list; fd_list = fd_list->next){ + if (fd_list->ino == ino && fd_list->version == n_version) break; + } + + if (fd_list->ino == ino) { + for(raw = fd_list->raw; raw && raw->next_in_ino; raw = raw->next_in_ino){ + offset = ref_offset(raw); + ret = jffs2_flash_read(c, offset, sizeof(findnode), &readlen,(char *)&findnode); + if (readlen != sizeof(findnode) || ret) { + up(&p->sem); + printk(KERN_WARNING"Error reading node from 0x%08x: %d\n",offset,ret); + return ret; + } + if ((je32_to_cpu(findnode.ino) == ino) && je16_to_cpu(findnode.nodetype) == JFFS2_NODETYPE_DIRENT) break; + } + } else { + up(&p->sem); + printk(KERN_WARNING"ea_get: can not find the file" ); + return -1; + } + up(&p->sem); + /* actually, we should check CRC */ + + /* end of CRC check */ + size = je32_to_cpu(findnode.totlen) - (findnode.nsize) - sizeof(findnode); + mem_size = max(size, min_size); + if (!mem_size) { + ea_buf->size = 0; + return 0; + } else { + /* allo`cate a buffer to work with */ + ea_buf->size = mem_size; + ea_buf->xattr = kmalloc(mem_size, GFP_KERNEL); + + if (ea_buf->xattr == NULL) { + return -ENOMEM; + } + /* copy rd EAs to ea_buf */ + offset = ref_offset(raw); + ret = jffs2_flash_read(c, offset + sizeof(findnode) + findnode.nsize, size, &readlen, (char *)ea_buf->xattr); + if (ret|| readlen != size){ + printk(KERN_WARNING"Error reading node form 0x%08x: %d\n", offset, ret); + return ret; + } + return size; + } +} + + +static int ea_put(struct dentry *parent, struct inode *inode, struct xattr_buffer *ea_buf, int new_size) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode), *p; + struct jffs2_raw_dirent *rd, findnode; + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd, *fd_list = NULL; + uint32_t direntlen, offset, alloclen, readlen; + int ret = 0; + char *name, *write_buf = NULL; + int namelen = 0; + uint32_t ino = f->inocache->ino; + int n_version =0 ; + + memset(&findnode, 0, sizeof(findnode)); + /* lookup the dirent list and find raw dirent*/ + p = JFFS2_INODE_INFO(parent->d_inode); + down(&p->sem); + + for(fd_list = p->dents; fd_list; fd_list = fd_list->next){ + if (fd_list->ino == ino && fd_list->version >= n_version) n_version = fd_list->version; + } + + for(fd_list = p->dents; fd_list; fd_list = fd_list->next) + if (fd_list->ino == ino && fd_list->version == fd_list->version) break; + + if (fd_list->ino == ino){ + for(raw = fd_list->raw; raw && raw->next_in_ino; raw = raw->next_in_ino) { + offset = ref_offset(raw); + ret = jffs2_flash_read(c, offset, sizeof(findnode), &readlen,(char *)&findnode); + if (ret||readlen != sizeof(findnode)) { + up(&p->sem); + printk(KERN_WARNING"Error reading node from 0x%08x: %d\n",offset,ret); + return ret; + } + if ((je32_to_cpu(findnode.ino) == ino) && je16_to_cpu(findnode.nodetype) == JFFS2_NODETYPE_DIRENT) break; + } + } else { + up(&p->sem); + printk(KERN_WARNING"ea_put: can not find the file" ); + return -1; + } + up(&p->sem); + + namelen = findnode.nsize; + + /* alloc write_buf for write flash */ + write_buf = kmalloc(namelen + new_size, GFP_KERNEL); + if (!write_buf) { + return -ENOMEM; + } + /* get the name in write_buf */ + ret = jffs2_flash_read(c, offset + sizeof(findnode), findnode.nsize, &readlen, write_buf); + if (ret||readlen != findnode.nsize){ + printk(KERN_WARNING"Error reading node form 0x%08x: %d\n", offset, ret); + return ret; + } + /* get the name in "name" */ + name = kmalloc(namelen + 1, GFP_KERNEL); + if (!name) { + if (write_buf) + kfree(write_buf); + return -ENOMEM; + } + memcpy(name, write_buf, namelen); + /* find the space for write */ + direntlen = sizeof(findnode) + namelen + new_size; + ret = jffs2_reserve_space(c, direntlen, &offset, &alloclen, ALLOC_NORMAL); + if (ret) { + return ret; + } + /* alloc the raw_dirent cache */ + rd = jffs2_alloc_raw_dirent(); + if (!rd) { + if (write_buf) + kfree(write_buf); + if (name) + kfree(name); + return -ENOMEM; + } + + down(&p->sem); + + rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); + rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd->totlen = cpu_to_je32(direntlen); + rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node) - 4)); + + rd->pino = cpu_to_je32(p->inocache->ino); + rd->version = cpu_to_je32(++p->highest_version); + rd->ino = findnode.ino; + rd->mctime = cpu_to_je32(get_seconds()); + rd->nsize = namelen; + rd->unused[0] = ea_buf->size; + /* fix me*/ + memcpy(&(write_buf[namelen]), ea_buf->xattr, new_size); + rd->type = findnode.type; + rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd) - 8)); + rd->name_crc = cpu_to_je32(crc32(0, write_buf, namelen)); + + fd = jffs2_write_dirent(c, p, rd, write_buf, namelen + new_size, offset, ALLOC_NORMAL); + /* restore the name */ + fd->nhash = full_name_hash(name, namelen); + fd->name[namelen] = 0; + if (IS_ERR(fd)) { + if (write_buf) + kfree(write_buf); + if (name) + kfree(name); + jffs2_free_raw_dirent(rd); + jffs2_complete_reservation(c); + up(&p->sem); + return PTR_ERR(fd); + } + jffs2_add_fd_to_list(c, fd, &p->dents); + jffs2_complete_reservation(c); + jffs2_free_raw_dirent(rd); + up(&p->sem); + if (write_buf) + kfree(write_buf); + if (name) + kfree(name); + + return 0; +} + + +static void ea_release(struct xattr_buffer *ea_buf) +{ + if (ea_buf->size) + kfree(ea_buf->xattr); +} +ssize_t jffs2_getxattr(struct dentry *dentry, const char *name, void *data, size_t buf_size) +{ + struct inode *inode = dentry->d_inode; + struct dentry *parent; + struct jffs2_xattr_list *ealist; + struct jffs2_xattr *ea; + struct xattr_buffer ea_buf; + int xattr_size; + ssize_t size; + int namelen = strlen(name); + char *value; + + if (name == NULL) + return -EINVAL; + + D1(printk(KERN_DEBUG"jffs2_getxattr: name=%s, buffer=%p, buffer_size=%ld",name,data,(long)buf_size)); + +// down_read(&JFFS2_INODE_INFO(inode)->xattr_sem); + parent = dentry->d_parent; + xattr_size = ea_get(parent, inode, &ea_buf, 0); + if (xattr_size < 0) { + size = xattr_size; + goto out; + } + + if (xattr_size == 0) { + goto not_found; + } + + ealist = (struct jffs2_xattr_list *)ea_buf.xattr; + + /* Find the named attribute*/ + for (ea=FIRST_XATTR(ealist); ea < END_XATTR(ealist,xattr_size); ea = NEXT_XATTR(ea)) + if ((namelen == ea->namelen) && + memcmp(name,ea->name,namelen)==0) { + /* Found */ + size = ea->valuelen; + if (!data) + goto release; + else if (size > buf_size) { + size = -ERANGE; + goto release; + } + value = ((char *)&ea->name) + ea->namelen + 1; + memcpy(data,value,size); + goto release; + } +not_found: + data = NULL; + size = -ENODATA; +release: + ea_release(&ea_buf); +out: +// up_read(&JFFS2_INODE_INFO(inode)->xattr_sem); + return size; +} + +int jffs2_setxattr(struct dentry *dentry, const char *name, const void *value, size_t value_len, int flags) +{ + struct inode *inode = dentry->d_inode; + struct dentry *parent; + struct jffs2_xattr_list *ealist; + struct jffs2_xattr *ea, *old_ea = NULL, *next_ea = NULL; + struct xattr_buffer ea_buf; + int found=0; + int xattr_size = 0; + int namelen = strlen(name); + int old_ea_size = 0; + int new_size; /* the size of new EAs */ + int length; + int ret; + + if (value == NULL || value_len == 0) { + value = ""; + value_len = 0; + } + + parent = dentry->d_parent; + /* read the inode's EAs */ + xattr_size = ea_get(parent, inode, &ea_buf, 0); + if (xattr_size < 0) { + ret = xattr_size; + goto out; + } + + again: + ealist = (struct jffs2_xattr_list *)ea_buf.xattr; + new_size = sizeof(struct jffs2_xattr_list); + + if (xattr_size) { + for (ea = FIRST_XATTR(ealist); ea < END_XATTR(ealist,xattr_size); ea = NEXT_XATTR(ea)) { + if ((namelen == ea->namelen) && + (memcmp(name, ea->name,namelen) == 0)) { + found = 1; + if (flags & XATTR_CREATE) { + ret = -EEXIST; + goto release; + } + old_ea = ea; + old_ea_size = JFFS2_XATTR_SIZE(ea); + next_ea = NEXT_XATTR(ea); + } else + new_size += JFFS2_XATTR_SIZE(ea); + } + } + + if (!found) { + if (flags & XATTR_REPLACE) { + ret = -ENODATA; + goto release; + } + if (value == NULL) { + ret = 0; + goto release; + } + } + if (value && value_len) + new_size += sizeof(struct jffs2_xattr) + namelen + 1 + value_len; + + /* compare with the size of current buffer */ + if (new_size > ea_buf.size) { + /* + * We need to reallocate more space for merged ea list + */ + ea_release(&ea_buf); + xattr_size = ea_get(parent, inode, &ea_buf, new_size); + if (xattr_size < 0) { + ret = xattr_size; + goto out; + } + goto again; + } + + /* remove the old ea of the same name */ + if (found) { + length = (char *)END_XATTR(ealist,xattr_size) - (char *)next_ea; + if (length > 0) + memmove(old_ea,next_ea,length); + xattr_size -= old_ea_size; + } + /* add new EA to the end */ + if (value && value_len) { + ea = (struct jffs2_xattr *)((char *)ealist + xattr_size); + ea->namelen = namelen; + ea->valuelen = value_len; + memcpy(ea->name, name, namelen); + ea->name[namelen] = 0; + if (value_len) + memcpy(ea->name + namelen + 1, value, value_len); + xattr_size += JFFS2_XATTR_SIZE(ea); + } + /* DEGUG*/ + if (new_size != xattr_size) { + printk(KERN_ERR"jffs2_setxattr: xattr_size = %d, new_size = %d\n", xattr_size, new_size); + ret = -EINVAL; + goto release; + } + if (new_size == sizeof(struct jffs2_xattr_list)) + new_size = 0; + + ret = ea_put(parent, inode, &ea_buf, new_size); + goto out; + release: + ea_release(&ea_buf); + out: + //up_write(&JFFS2_INODE_INFO(inode)->xattr_sem); + return ret; +} + +ssize_t jffs2_listxattr(struct dentry *dentry, char *data, size_t buf_size) +{ + struct inode *inode = dentry->d_inode; + struct dentry *parent; + char *buffer; + ssize_t size = 0; + int xattr_size; + struct jffs2_xattr_list *ealist; + struct jffs2_xattr *ea; + struct xattr_buffer ea_buf; + + memset(data, 0, buf_size); + //down_read(&JFFS2_INODE_INFO(inode)->xattr_sem); + parent = dentry->d_parent; + xattr_size = ea_get(parent, inode, &ea_buf, 0); + if (xattr_size < 0) { + size = xattr_size; + goto out; + } + if (xattr_size == 0) { + goto release; + } + + ealist = (struct jffs2_xattr_list *)ea_buf.xattr; + + for (ea = FIRST_XATTR(ealist); ea < END_XATTR(ealist, xattr_size); ea = NEXT_XATTR(ea)) + size += ea->namelen +1; + + if (!data) + goto release; + + if (size > buf_size) { + size = -ERANGE; + goto release; + } + buffer = data; + for (ea = FIRST_XATTR(ealist); ea < END_XATTR(ealist, xattr_size); ea = NEXT_XATTR(ea)) { + memcpy(buffer, ea->name, ea->namelen); + buffer[ea->namelen] = '\\'; + buffer += ea->namelen +1; + } + release: + ea_release(&ea_buf); + out: +// up_read(&JFFS2_INODE_INFO(inode)->xattr_sem); + return size; +} + +int jffs2_removexattr(struct dentry *dentry, const char *name) +{ + return jffs2_setxattr(dentry, name, NULL, 0, XATTR_REPLACE); +} diff -uNr linux-2.6.10.mxc/fs/Kconfig linux-2.6.10.jffs2/fs/Kconfig --- linux-2.6.10.mxc/fs/Kconfig 2005-08-19 15:40:26.000000000 +0800 +++ linux-2.6.10.jffs2/fs/Kconfig 2005-08-19 15:50:52.000000000 +0800 @@ -1201,6 +1201,12 @@ Further information on the design and implementation of JFFS2 is available at . +config JFFS2_FS_XATTR + bool "JFFS2 extended attributes" + depends on JFFS2_FS + help + JFFS2 extended attributes + config JFFS2_FS_DEBUG int "JFFS2 debugging verbosity (0 = quiet, 2 = noisy)" depends on JFFS2_FS diff -uNr linux-2.6.10.mxc/include/linux/jffs2_xattr.h linux-2.6.10.jffs2/include/linux/jffs2_xattr.h --- linux-2.6.10.mxc/include/linux/jffs2_xattr.h 1970-01-01 08:00:00.000000000 +0800 +++ linux-2.6.10.jffs2/include/linux/jffs2_xattr.h 2005-08-19 16:20:18.000000000 +0800 @@ -0,0 +1,59 @@ +/* + * + * Copyright 2005 Motorola, Inc. All Rights Reserved. + * + * Created by Ma yun + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _JFFS2_XATTR +#define _JFFS2_XATTR + +/* jffs2_xattr describe the on-disk format of the extended attributes. + * + */ +struct jffs2_xattr { + uint8_t namelen; /* Length of name */ + uint8_t valuelen; /* Length of value */ + char name[0]; /* Attribute name (include null-terminator)*/ + /* Attribute Valude immediately follows name */ +}__attribute__((packed)); + +struct jffs2_xattr_list { + struct jffs2_xattr xattr[0]; /* EAs list */ +}__attribute__((packed)); + + +/* + * Some macros for dealing with variable length EA lists. + */ +#define JFFS2_XATTR_SIZE(xattr) \ + (sizeof(struct jffs2_xattr) + (xattr)->namelen + 1 + \ + (xattr)->valuelen) +#define NEXT_XATTR(xattr) \ + ((struct jffs2_xattr *)(((char *)(xattr)) + (JFFS2_XATTR_SIZE(xattr)))) +#define FIRST_XATTR(xattr_list) \ + ((xattr_list)->xattr) +#define END_XATTR(xattr_list,size) \ + ((struct jffs2_xattr *)(((char *)(xattr_list)) + size)) + +extern int jffs2_setxattr(struct dentry *, const char *, const void *, size_t, int); +extern ssize_t jffs2_getxattr(struct dentry *, const char *, void *, size_t); +extern ssize_t jffs2_listxattr(struct dentry *, char *, size_t); +extern int jffs2_removexattr(struct dentry *, const char *); + +#endif +