cregit-Linux how code gets into the kernel

Release 4.10 fs/jffs2/xattr.c

Directory: fs/jffs2
/*
 * 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.
 *
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt


#define JFFS2_XATTR_IS_CORRUPTED	1

#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/posix_acl_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. Currently, 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); }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei49100.00%1100.00%
Total49100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei85100.00%1100.00%
Total85100.00%1100.00%


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", __func__, 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; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei9198.91%150.00%
harvey harrisonharvey harrison11.09%150.00%
Total92100.00%2100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei167100.00%1100.00%
Total167100.00%1100.00%


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 JFFS2_XATTR_IS_CORRUPTED; } 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 JFFS2_XATTR_IS_CORRUPTED; } 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 verifying xdatum (xid=%u, version=%u)\n", xd->xid, xd->version); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei53099.07%350.00%
david woodhousedavid woodhouse20.37%116.67%
jean-christophe duboisjean-christophe dubois20.37%116.67%
masanari iidamasanari iida10.19%116.67%
Total535100.00%6100.00%


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_XATTR)" " 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 JFFS2_XATTR_IS_CORRUPTED; } 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; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei34599.14%250.00%
jean-christophe duboisjean-christophe dubois20.57%125.00%
david woodhousedavid woodhouse10.29%125.00%
Total348100.00%4100.00%


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 JFFS2_XATTR_IS_CORRUPTED; 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; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei8898.88%375.00%
jean-christophe duboisjean-christophe dubois11.12%125.00%
Total89100.00%4100.00%


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 = &rx; 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; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei37790.84%337.50%
david woodhousedavid woodhouse389.16%562.50%
Total415100.00%8100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei377100.00%2100.00%
Total377100.00%2100.00%


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); } }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei11196.52%583.33%
jeff garzikjeff garzik43.48%116.67%
Total115100.00%6100.00%

/* -------- 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. * jffs2_xattr_do_crccheck_inode(c, ic) * is used to force xattr data integrity check during the initial gc scan. * -------------------------------------------------- */
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; int rc; spin_lock(&c->erase_completion_lock); if (ref_flags(ref->node) != REF_UNCHECKED) goto complete; offset = ref_offset(ref->node); spin_unlock(&c->erase_completion_lock); rc = jffs2_flash_read(c, offset, sizeof(rr), &readlen, (char *)&rr); if (rc || sizeof(rr) != readlen) { JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu, at %#08x\n", rc, sizeof(rr), readlen, offset); return rc ? rc : -EIO; } /* obsolete node */ crc = crc32(0, &rr, sizeof(rr) - 4); if (crc != je32_to_cpu(rr.node_crc)) { JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", offset, je32_to_cpu(rr.node_crc), crc); return JFFS2_XATTR_IS_CORRUPTED; } 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/%zu\n", offset, 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 JFFS2_XATTR_IS_CORRUPTED; } ref->ino = je32_to_cpu(rr.ino); ref->xid = je32_to_cpu(rr.xid); ref->xseqno = je32_to_cpu(rr.xseqno); if (ref->xseqno > c->highest_xseqno) c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER); spin_lock(&c->erase_completion_lock); complete: for (raw=ref->node; raw != (void *)ref; 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) | ((ref->node==raw) ? REF_PRISTINE : REF_NORMAL); } 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; }

Contributors

PersonTokensPropCommitsCommitProp
kaigai koheikaigai kohei48099.17%250.00%
david woodhousedavid woodhouse20.41%125.00%
jean-christophe duboisjean-christophe dubois20.41%125.00%
Total484100.00%4100.00%


static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) { /* must be called under down_write(xattr_sem) */ struct jffs2_raw_xref rr; size_t length; uint32_t xseqno, phys_ofs = write_ofs(c); int ret; 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)); xseqno = (c->highest_xseqno += 2)