📄 xattr.c
字号:
/* * JFFS2 -- Journalling Flash File System, Version 2. * * Copyright © 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). * is_xattr_datum_unchecked(c, xd) * returns 1, if xdatum contains any unchecked raw nodes. if all raw nodes are not * unchecked, it returns 0. * 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. * 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) * is used to write xdatum to medium. xd->version will be incremented. * create_xattr_datum(c, xprefix, xname, xvalue, xsize) * is used to create new xdatum and write to medium. * unrefer_xattr_datum(c, xd) * is used to delete a xdatum. When nobody refers this xdatum, JFFS2_XFLAGS_DEAD * is set on xd->flags and chained xattr_dead_list or release it immediately. * In the first case, the garbage collector release it later. * -------------------------------------------------- */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 int is_xattr_datum_unchecked(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd){ struct jffs2_raw_node_ref *raw; int rc = 0; spin_lock(&c->erase_completion_lock); for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) { if (ref_flags(raw) == REF_UNCHECKED) { rc = 1; break; } } spin_unlock(&c->erase_completion_lock); return rc;}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 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_node_ref *raw; struct jffs2_raw_xattr rx; size_t readlen; uint32_t crc, offset, totlen; int rc; spin_lock(&c->erase_completion_lock); offset = ref_offset(xd->node); if (ref_flags(xd->node) == REF_PRISTINE) goto complete; spin_unlock(&c->erase_completion_lock); rc = jffs2_flash_read(c, offset, sizeof(rx), &readlen, (char *)&rx); if (rc || readlen != sizeof(rx)) { JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n", rc, sizeof(rx), readlen, offset); return rc ? rc : -EIO; } crc = crc32(0, &rx, sizeof(rx) - 4); if (crc != je32_to_cpu(rx.node_crc)) { JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", offset, je32_to_cpu(rx.hdr_crc), crc); xd->flags |= JFFS2_XFLAGS_INVALID; 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", offset, 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); xd->flags |= JFFS2_XFLAGS_INVALID; 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); spin_lock(&c->erase_completion_lock); complete: for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) { jeb = &c->blocks[ref_offset(raw) / c->sector_size]; totlen = PAD(ref_totlen(c, jeb, raw)); if (ref_flags(raw) == REF_UNCHECKED) { c->unchecked_size -= totlen; c->used_size += totlen; jeb->unchecked_size -= totlen; jeb->used_size += totlen; } raw->flash_offset = ref_offset(raw) | ((xd->node==raw) ? REF_PRISTINE : REF_NORMAL); } 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(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=%zu, 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); xd->flags |= JFFS2_XFLAGS_INVALID; 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->flags & JFFS2_XFLAGS_DEAD); if (xd->xname) return 0; if (xd->flags & JFFS2_XFLAGS_INVALID) return EIO; if (unlikely(is_xattr_datum_unchecked(c, xd))) rc = do_verify_xattr_datum(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){ /* must be called under down_write(xattr_sem) */ struct jffs2_raw_xattr rx; struct kvec vecs[2]; size_t length; int rc, totlen; uint32_t phys_ofs = write_ofs(c); BUG_ON(!xd->xname); BUG_ON(xd->flags & (JFFS2_XFLAGS_DEAD|JFFS2_XFLAGS_INVALID)); vecs[0].iov_base = ℞ vecs[0].iov_len = 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; /* Setup raw-xattr */ memset(&rx, 0, sizeof(rx)); 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=%zu, at %#08x\n", rc, totlen, length, phys_ofs); rc = rc ? rc : -EIO; if (length) jffs2_add_physical_node_ref(c, phys_ofs | REF_OBSOLETE, PAD(totlen), NULL); return rc; } /* success */ jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(totlen), (void *)xd); 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){ /* 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)) { atomic_inc(&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); atomic_set(&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); 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;}static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd){ /* must be called under down_write(xattr_sem) */ if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) { unload_xattr_datum(c, xd); xd->flags |= JFFS2_XFLAGS_DEAD; if (xd->node == (void *)xd) { BUG_ON(!(xd->flags & JFFS2_XFLAGS_INVALID)); jffs2_free_xattr_datum(xd); } else { list_add(&xd->xindex, &c->xattr_dead_list); } spin_unlock(&c->erase_completion_lock); dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xd->xid, xd->version); }}/* -------- 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. * save_xattr_ref(c, ref) * is used to write xref to medium. If delete marker is marked, it write * a delete marker of xref into medium. * create_xattr_ref(c, ic, xd) * is used to create a new xref and write to medium. * delete_xattr_ref(c, ref) * is used to delete jffs2_xattr_ref. It marks xref XREF_DELETE_MARKER, * and allows GC to reclaim those physical nodes. * 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_node_ref *raw; struct jffs2_raw_xref rr; size_t readlen; uint32_t crc, offset, totlen;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -