203a55f04f
When insmod ubifs.ko, a kmemleak reported as below:
unreferenced object 0xffff88817fb1a780 (size 8):
comm "insmod", pid 25265, jiffies 4295239702 (age 100.130s)
hex dump (first 8 bytes):
75 62 69 66 73 00 ff ff ubifs...
backtrace:
[<ffffffff81b3fc4c>] slab_post_alloc_hook+0x9c/0x3c0
[<ffffffff81b44bf3>] __kmalloc_track_caller+0x183/0x410
[<ffffffff8198d3da>] kstrdup+0x3a/0x80
[<ffffffff8198d486>] kstrdup_const+0x66/0x80
[<ffffffff83989325>] kvasprintf_const+0x155/0x190
[<ffffffff83bf55bb>] kobject_set_name_vargs+0x5b/0x150
[<ffffffff83bf576b>] kobject_set_name+0xbb/0xf0
[<ffffffff8100204c>] do_one_initcall+0x14c/0x5a0
[<ffffffff8157e380>] do_init_module+0x1f0/0x660
[<ffffffff815857be>] load_module+0x6d7e/0x7590
[<ffffffff8158644f>] __do_sys_finit_module+0x19f/0x230
[<ffffffff815866b3>] __x64_sys_finit_module+0x73/0xb0
[<ffffffff88c98e85>] do_syscall_64+0x35/0x80
[<ffffffff88e00087>] entry_SYSCALL_64_after_hwframe+0x63/0xcd
When kset_register() failed, we should call kset_put to cleanup it.
Fixes: 2e3cbf4258
("ubifs: Export filesystem error counters")
Signed-off-by: Liu Shixin <liushixin2@huawei.com>
Signed-off-by: Richard Weinberger <richard@nod.at>
157 lines
3.2 KiB
C
157 lines
3.2 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* This file is part of UBIFS.
|
|
*
|
|
* Copyright (C) 2021 Cisco Systems
|
|
*
|
|
* Author: Stefan Schaeckeler
|
|
*/
|
|
|
|
|
|
#include <linux/fs.h>
|
|
#include "ubifs.h"
|
|
|
|
enum attr_id_t {
|
|
attr_errors_magic,
|
|
attr_errors_node,
|
|
attr_errors_crc,
|
|
};
|
|
|
|
struct ubifs_attr {
|
|
struct attribute attr;
|
|
enum attr_id_t attr_id;
|
|
};
|
|
|
|
#define UBIFS_ATTR(_name, _mode, _id) \
|
|
static struct ubifs_attr ubifs_attr_##_name = { \
|
|
.attr = {.name = __stringify(_name), .mode = _mode }, \
|
|
.attr_id = attr_##_id, \
|
|
}
|
|
|
|
#define UBIFS_ATTR_FUNC(_name, _mode) UBIFS_ATTR(_name, _mode, _name)
|
|
|
|
UBIFS_ATTR_FUNC(errors_magic, 0444);
|
|
UBIFS_ATTR_FUNC(errors_crc, 0444);
|
|
UBIFS_ATTR_FUNC(errors_node, 0444);
|
|
|
|
#define ATTR_LIST(name) (&ubifs_attr_##name.attr)
|
|
|
|
static struct attribute *ubifs_attrs[] = {
|
|
ATTR_LIST(errors_magic),
|
|
ATTR_LIST(errors_node),
|
|
ATTR_LIST(errors_crc),
|
|
NULL,
|
|
};
|
|
ATTRIBUTE_GROUPS(ubifs);
|
|
|
|
static ssize_t ubifs_attr_show(struct kobject *kobj,
|
|
struct attribute *attr, char *buf)
|
|
{
|
|
struct ubifs_info *sbi = container_of(kobj, struct ubifs_info,
|
|
kobj);
|
|
|
|
struct ubifs_attr *a = container_of(attr, struct ubifs_attr, attr);
|
|
|
|
switch (a->attr_id) {
|
|
case attr_errors_magic:
|
|
return sysfs_emit(buf, "%u\n", sbi->stats->magic_errors);
|
|
case attr_errors_node:
|
|
return sysfs_emit(buf, "%u\n", sbi->stats->node_errors);
|
|
case attr_errors_crc:
|
|
return sysfs_emit(buf, "%u\n", sbi->stats->crc_errors);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
static void ubifs_sb_release(struct kobject *kobj)
|
|
{
|
|
struct ubifs_info *c = container_of(kobj, struct ubifs_info, kobj);
|
|
|
|
complete(&c->kobj_unregister);
|
|
}
|
|
|
|
static const struct sysfs_ops ubifs_attr_ops = {
|
|
.show = ubifs_attr_show,
|
|
};
|
|
|
|
static struct kobj_type ubifs_sb_ktype = {
|
|
.default_groups = ubifs_groups,
|
|
.sysfs_ops = &ubifs_attr_ops,
|
|
.release = ubifs_sb_release,
|
|
};
|
|
|
|
static struct kobj_type ubifs_ktype = {
|
|
.sysfs_ops = &ubifs_attr_ops,
|
|
};
|
|
|
|
static struct kset ubifs_kset = {
|
|
.kobj = {.ktype = &ubifs_ktype},
|
|
};
|
|
|
|
int ubifs_sysfs_register(struct ubifs_info *c)
|
|
{
|
|
int ret, n;
|
|
char dfs_dir_name[UBIFS_DFS_DIR_LEN+1];
|
|
|
|
c->stats = kzalloc(sizeof(struct ubifs_stats_info), GFP_KERNEL);
|
|
if (!c->stats) {
|
|
ret = -ENOMEM;
|
|
goto out_last;
|
|
}
|
|
n = snprintf(dfs_dir_name, UBIFS_DFS_DIR_LEN + 1, UBIFS_DFS_DIR_NAME,
|
|
c->vi.ubi_num, c->vi.vol_id);
|
|
|
|
if (n > UBIFS_DFS_DIR_LEN) {
|
|
/* The array size is too small */
|
|
ret = -EINVAL;
|
|
goto out_free;
|
|
}
|
|
|
|
c->kobj.kset = &ubifs_kset;
|
|
init_completion(&c->kobj_unregister);
|
|
|
|
ret = kobject_init_and_add(&c->kobj, &ubifs_sb_ktype, NULL,
|
|
"%s", dfs_dir_name);
|
|
if (ret)
|
|
goto out_put;
|
|
|
|
return 0;
|
|
|
|
out_put:
|
|
kobject_put(&c->kobj);
|
|
wait_for_completion(&c->kobj_unregister);
|
|
out_free:
|
|
kfree(c->stats);
|
|
out_last:
|
|
ubifs_err(c, "cannot create sysfs entry for ubifs%d_%d, error %d\n",
|
|
c->vi.ubi_num, c->vi.vol_id, ret);
|
|
return ret;
|
|
}
|
|
|
|
void ubifs_sysfs_unregister(struct ubifs_info *c)
|
|
{
|
|
kobject_del(&c->kobj);
|
|
kobject_put(&c->kobj);
|
|
wait_for_completion(&c->kobj_unregister);
|
|
|
|
kfree(c->stats);
|
|
}
|
|
|
|
int __init ubifs_sysfs_init(void)
|
|
{
|
|
int ret;
|
|
|
|
kobject_set_name(&ubifs_kset.kobj, "ubifs");
|
|
ubifs_kset.kobj.parent = fs_kobj;
|
|
ret = kset_register(&ubifs_kset);
|
|
if (ret)
|
|
kset_put(&ubifs_kset);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void ubifs_sysfs_exit(void)
|
|
{
|
|
kset_unregister(&ubifs_kset);
|
|
}
|