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