cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

filecheck.c (12199B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * filecheck.c
      4 *
      5 * Code which implements online file check.
      6 *
      7 * Copyright (C) 2016 SuSE.  All rights reserved.
      8 */
      9
     10#include <linux/list.h>
     11#include <linux/spinlock.h>
     12#include <linux/module.h>
     13#include <linux/slab.h>
     14#include <linux/kmod.h>
     15#include <linux/fs.h>
     16#include <linux/kobject.h>
     17#include <linux/sysfs.h>
     18#include <linux/sysctl.h>
     19#include <cluster/masklog.h>
     20
     21#include "ocfs2.h"
     22#include "ocfs2_fs.h"
     23#include "stackglue.h"
     24#include "inode.h"
     25
     26#include "filecheck.h"
     27
     28
     29/* File check error strings,
     30 * must correspond with error number in header file.
     31 */
     32static const char * const ocfs2_filecheck_errs[] = {
     33	"SUCCESS",
     34	"FAILED",
     35	"INPROGRESS",
     36	"READONLY",
     37	"INJBD",
     38	"INVALIDINO",
     39	"BLOCKECC",
     40	"BLOCKNO",
     41	"VALIDFLAG",
     42	"GENERATION",
     43	"UNSUPPORTED"
     44};
     45
     46struct ocfs2_filecheck_entry {
     47	struct list_head fe_list;
     48	unsigned long fe_ino;
     49	unsigned int fe_type;
     50	unsigned int fe_done:1;
     51	unsigned int fe_status:31;
     52};
     53
     54struct ocfs2_filecheck_args {
     55	unsigned int fa_type;
     56	union {
     57		unsigned long fa_ino;
     58		unsigned int fa_len;
     59	};
     60};
     61
     62static const char *
     63ocfs2_filecheck_error(int errno)
     64{
     65	if (!errno)
     66		return ocfs2_filecheck_errs[errno];
     67
     68	BUG_ON(errno < OCFS2_FILECHECK_ERR_START ||
     69	       errno > OCFS2_FILECHECK_ERR_END);
     70	return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1];
     71}
     72
     73static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
     74					struct kobj_attribute *attr,
     75					char *buf);
     76static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
     77					struct kobj_attribute *attr,
     78					const char *buf, size_t count);
     79static struct kobj_attribute ocfs2_filecheck_attr_chk =
     80					__ATTR(check, S_IRUSR | S_IWUSR,
     81					ocfs2_filecheck_attr_show,
     82					ocfs2_filecheck_attr_store);
     83static struct kobj_attribute ocfs2_filecheck_attr_fix =
     84					__ATTR(fix, S_IRUSR | S_IWUSR,
     85					ocfs2_filecheck_attr_show,
     86					ocfs2_filecheck_attr_store);
     87static struct kobj_attribute ocfs2_filecheck_attr_set =
     88					__ATTR(set, S_IRUSR | S_IWUSR,
     89					ocfs2_filecheck_attr_show,
     90					ocfs2_filecheck_attr_store);
     91static struct attribute *ocfs2_filecheck_attrs[] = {
     92	&ocfs2_filecheck_attr_chk.attr,
     93	&ocfs2_filecheck_attr_fix.attr,
     94	&ocfs2_filecheck_attr_set.attr,
     95	NULL
     96};
     97ATTRIBUTE_GROUPS(ocfs2_filecheck);
     98
     99static void ocfs2_filecheck_release(struct kobject *kobj)
    100{
    101	struct ocfs2_filecheck_sysfs_entry *entry = container_of(kobj,
    102				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
    103
    104	complete(&entry->fs_kobj_unregister);
    105}
    106
    107static ssize_t
    108ocfs2_filecheck_show(struct kobject *kobj, struct attribute *attr, char *buf)
    109{
    110	ssize_t ret = -EIO;
    111	struct kobj_attribute *kattr = container_of(attr,
    112					struct kobj_attribute, attr);
    113
    114	kobject_get(kobj);
    115	if (kattr->show)
    116		ret = kattr->show(kobj, kattr, buf);
    117	kobject_put(kobj);
    118	return ret;
    119}
    120
    121static ssize_t
    122ocfs2_filecheck_store(struct kobject *kobj, struct attribute *attr,
    123			const char *buf, size_t count)
    124{
    125	ssize_t ret = -EIO;
    126	struct kobj_attribute *kattr = container_of(attr,
    127					struct kobj_attribute, attr);
    128
    129	kobject_get(kobj);
    130	if (kattr->store)
    131		ret = kattr->store(kobj, kattr, buf, count);
    132	kobject_put(kobj);
    133	return ret;
    134}
    135
    136static const struct sysfs_ops ocfs2_filecheck_ops = {
    137	.show = ocfs2_filecheck_show,
    138	.store = ocfs2_filecheck_store,
    139};
    140
    141static struct kobj_type ocfs2_ktype_filecheck = {
    142	.default_groups = ocfs2_filecheck_groups,
    143	.sysfs_ops = &ocfs2_filecheck_ops,
    144	.release = ocfs2_filecheck_release,
    145};
    146
    147static void
    148ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry)
    149{
    150	struct ocfs2_filecheck_entry *p;
    151
    152	spin_lock(&entry->fs_fcheck->fc_lock);
    153	while (!list_empty(&entry->fs_fcheck->fc_head)) {
    154		p = list_first_entry(&entry->fs_fcheck->fc_head,
    155				     struct ocfs2_filecheck_entry, fe_list);
    156		list_del(&p->fe_list);
    157		BUG_ON(!p->fe_done); /* To free a undone file check entry */
    158		kfree(p);
    159	}
    160	spin_unlock(&entry->fs_fcheck->fc_lock);
    161
    162	kfree(entry->fs_fcheck);
    163	entry->fs_fcheck = NULL;
    164}
    165
    166int ocfs2_filecheck_create_sysfs(struct ocfs2_super *osb)
    167{
    168	int ret;
    169	struct ocfs2_filecheck *fcheck;
    170	struct ocfs2_filecheck_sysfs_entry *entry = &osb->osb_fc_ent;
    171
    172	fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS);
    173	if (!fcheck)
    174		return -ENOMEM;
    175
    176	INIT_LIST_HEAD(&fcheck->fc_head);
    177	spin_lock_init(&fcheck->fc_lock);
    178	fcheck->fc_max = OCFS2_FILECHECK_MINSIZE;
    179	fcheck->fc_size = 0;
    180	fcheck->fc_done = 0;
    181
    182	entry->fs_kobj.kset = osb->osb_dev_kset;
    183	init_completion(&entry->fs_kobj_unregister);
    184	ret = kobject_init_and_add(&entry->fs_kobj, &ocfs2_ktype_filecheck,
    185					NULL, "filecheck");
    186	if (ret) {
    187		kobject_put(&entry->fs_kobj);
    188		kfree(fcheck);
    189		return ret;
    190	}
    191
    192	entry->fs_fcheck = fcheck;
    193	return 0;
    194}
    195
    196void ocfs2_filecheck_remove_sysfs(struct ocfs2_super *osb)
    197{
    198	if (!osb->osb_fc_ent.fs_fcheck)
    199		return;
    200
    201	kobject_del(&osb->osb_fc_ent.fs_kobj);
    202	kobject_put(&osb->osb_fc_ent.fs_kobj);
    203	wait_for_completion(&osb->osb_fc_ent.fs_kobj_unregister);
    204	ocfs2_filecheck_sysfs_free(&osb->osb_fc_ent);
    205}
    206
    207static int
    208ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
    209			      unsigned int count);
    210static int
    211ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
    212			   unsigned int len)
    213{
    214	int ret;
    215
    216	if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
    217		return -EINVAL;
    218
    219	spin_lock(&ent->fs_fcheck->fc_lock);
    220	if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
    221		mlog(ML_NOTICE,
    222		"Cannot set online file check maximum entry number "
    223		"to %u due to too many pending entries(%u)\n",
    224		len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
    225		ret = -EBUSY;
    226	} else {
    227		if (len < ent->fs_fcheck->fc_size)
    228			BUG_ON(!ocfs2_filecheck_erase_entries(ent,
    229				ent->fs_fcheck->fc_size - len));
    230
    231		ent->fs_fcheck->fc_max = len;
    232		ret = 0;
    233	}
    234	spin_unlock(&ent->fs_fcheck->fc_lock);
    235
    236	return ret;
    237}
    238
    239#define OCFS2_FILECHECK_ARGS_LEN	24
    240static int
    241ocfs2_filecheck_args_get_long(const char *buf, size_t count,
    242			      unsigned long *val)
    243{
    244	char buffer[OCFS2_FILECHECK_ARGS_LEN];
    245
    246	memcpy(buffer, buf, count);
    247	buffer[count] = '\0';
    248
    249	if (kstrtoul(buffer, 0, val))
    250		return 1;
    251
    252	return 0;
    253}
    254
    255static int
    256ocfs2_filecheck_type_parse(const char *name, unsigned int *type)
    257{
    258	if (!strncmp(name, "fix", 4))
    259		*type = OCFS2_FILECHECK_TYPE_FIX;
    260	else if (!strncmp(name, "check", 6))
    261		*type = OCFS2_FILECHECK_TYPE_CHK;
    262	else if (!strncmp(name, "set", 4))
    263		*type = OCFS2_FILECHECK_TYPE_SET;
    264	else
    265		return 1;
    266
    267	return 0;
    268}
    269
    270static int
    271ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
    272			   struct ocfs2_filecheck_args *args)
    273{
    274	unsigned long val = 0;
    275	unsigned int type;
    276
    277	/* too short/long args length */
    278	if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
    279		return 1;
    280
    281	if (ocfs2_filecheck_type_parse(name, &type))
    282		return 1;
    283	if (ocfs2_filecheck_args_get_long(buf, count, &val))
    284		return 1;
    285
    286	if (val <= 0)
    287		return 1;
    288
    289	args->fa_type = type;
    290	if (type == OCFS2_FILECHECK_TYPE_SET)
    291		args->fa_len = (unsigned int)val;
    292	else
    293		args->fa_ino = val;
    294
    295	return 0;
    296}
    297
    298static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
    299				    struct kobj_attribute *attr,
    300				    char *buf)
    301{
    302
    303	ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
    304	unsigned int type;
    305	struct ocfs2_filecheck_entry *p;
    306	struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
    307				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
    308
    309	if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
    310		return -EINVAL;
    311
    312	if (type == OCFS2_FILECHECK_TYPE_SET) {
    313		spin_lock(&ent->fs_fcheck->fc_lock);
    314		total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
    315		spin_unlock(&ent->fs_fcheck->fc_lock);
    316		goto exit;
    317	}
    318
    319	ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
    320	total += ret;
    321	remain -= ret;
    322	spin_lock(&ent->fs_fcheck->fc_lock);
    323	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
    324		if (p->fe_type != type)
    325			continue;
    326
    327		ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
    328			       p->fe_ino, p->fe_done,
    329			       ocfs2_filecheck_error(p->fe_status));
    330		if (ret >= remain) {
    331			/* snprintf() didn't fit */
    332			total = -E2BIG;
    333			break;
    334		}
    335		total += ret;
    336		remain -= ret;
    337	}
    338	spin_unlock(&ent->fs_fcheck->fc_lock);
    339
    340exit:
    341	return total;
    342}
    343
    344static inline int
    345ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry *ent,
    346				unsigned long ino)
    347{
    348	struct ocfs2_filecheck_entry *p;
    349
    350	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
    351		if (!p->fe_done) {
    352			if (p->fe_ino == ino)
    353				return 1;
    354		}
    355	}
    356
    357	return 0;
    358}
    359
    360static inline int
    361ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
    362{
    363	struct ocfs2_filecheck_entry *p;
    364
    365	list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
    366		if (p->fe_done) {
    367			list_del(&p->fe_list);
    368			kfree(p);
    369			ent->fs_fcheck->fc_size--;
    370			ent->fs_fcheck->fc_done--;
    371			return 1;
    372		}
    373	}
    374
    375	return 0;
    376}
    377
    378static int
    379ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
    380			      unsigned int count)
    381{
    382	unsigned int i = 0;
    383	unsigned int ret = 0;
    384
    385	while (i++ < count) {
    386		if (ocfs2_filecheck_erase_entry(ent))
    387			ret++;
    388		else
    389			break;
    390	}
    391
    392	return (ret == count ? 1 : 0);
    393}
    394
    395static void
    396ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
    397			   struct ocfs2_filecheck_entry *entry)
    398{
    399	spin_lock(&ent->fs_fcheck->fc_lock);
    400	entry->fe_done = 1;
    401	ent->fs_fcheck->fc_done++;
    402	spin_unlock(&ent->fs_fcheck->fc_lock);
    403}
    404
    405static unsigned int
    406ocfs2_filecheck_handle(struct ocfs2_super *osb,
    407		       unsigned long ino, unsigned int flags)
    408{
    409	unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
    410	struct inode *inode = NULL;
    411	int rc;
    412
    413	inode = ocfs2_iget(osb, ino, flags, 0);
    414	if (IS_ERR(inode)) {
    415		rc = (int)(-(long)inode);
    416		if (rc >= OCFS2_FILECHECK_ERR_START &&
    417		    rc < OCFS2_FILECHECK_ERR_END)
    418			ret = rc;
    419		else
    420			ret = OCFS2_FILECHECK_ERR_FAILED;
    421	} else
    422		iput(inode);
    423
    424	return ret;
    425}
    426
    427static void
    428ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
    429			     struct ocfs2_filecheck_entry *entry)
    430{
    431	struct ocfs2_super *osb = container_of(ent, struct ocfs2_super,
    432						osb_fc_ent);
    433
    434	if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
    435		entry->fe_status = ocfs2_filecheck_handle(osb,
    436				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
    437	else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
    438		entry->fe_status = ocfs2_filecheck_handle(osb,
    439				entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
    440	else
    441		entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
    442
    443	ocfs2_filecheck_done_entry(ent, entry);
    444}
    445
    446static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
    447				     struct kobj_attribute *attr,
    448				     const char *buf, size_t count)
    449{
    450	ssize_t ret = 0;
    451	struct ocfs2_filecheck_args args;
    452	struct ocfs2_filecheck_entry *entry;
    453	struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
    454				struct ocfs2_filecheck_sysfs_entry, fs_kobj);
    455
    456	if (count == 0)
    457		return count;
    458
    459	if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args))
    460		return -EINVAL;
    461
    462	if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
    463		ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
    464		goto exit;
    465	}
    466
    467	entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
    468	if (!entry) {
    469		ret = -ENOMEM;
    470		goto exit;
    471	}
    472
    473	spin_lock(&ent->fs_fcheck->fc_lock);
    474	if (ocfs2_filecheck_is_dup_entry(ent, args.fa_ino)) {
    475		ret = -EEXIST;
    476		kfree(entry);
    477	} else if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
    478		(ent->fs_fcheck->fc_done == 0)) {
    479		mlog(ML_NOTICE,
    480		"Cannot do more file check "
    481		"since file check queue(%u) is full now\n",
    482		ent->fs_fcheck->fc_max);
    483		ret = -EAGAIN;
    484		kfree(entry);
    485	} else {
    486		if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
    487		    (ent->fs_fcheck->fc_done > 0)) {
    488			/* Delete the oldest entry which was done,
    489			 * make sure the entry size in list does
    490			 * not exceed maximum value
    491			 */
    492			BUG_ON(!ocfs2_filecheck_erase_entry(ent));
    493		}
    494
    495		entry->fe_ino = args.fa_ino;
    496		entry->fe_type = args.fa_type;
    497		entry->fe_done = 0;
    498		entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
    499		list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
    500		ent->fs_fcheck->fc_size++;
    501	}
    502	spin_unlock(&ent->fs_fcheck->fc_lock);
    503
    504	if (!ret)
    505		ocfs2_filecheck_handle_entry(ent, entry);
    506
    507exit:
    508	return (!ret ? count : ret);
    509}