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