Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * filecheck.c
0004  *
0005  * Code which implements online file check.
0006  *
0007  * Copyright (C) 2016 SuSE.  All rights reserved.
0008  */
0009 
0010 #include <linux/list.h>
0011 #include <linux/spinlock.h>
0012 #include <linux/module.h>
0013 #include <linux/slab.h>
0014 #include <linux/kmod.h>
0015 #include <linux/fs.h>
0016 #include <linux/kobject.h>
0017 #include <linux/sysfs.h>
0018 #include <linux/sysctl.h>
0019 #include <cluster/masklog.h>
0020 
0021 #include "ocfs2.h"
0022 #include "ocfs2_fs.h"
0023 #include "stackglue.h"
0024 #include "inode.h"
0025 
0026 #include "filecheck.h"
0027 
0028 
0029 /* File check error strings,
0030  * must correspond with error number in header file.
0031  */
0032 static const char * const ocfs2_filecheck_errs[] = {
0033     "SUCCESS",
0034     "FAILED",
0035     "INPROGRESS",
0036     "READONLY",
0037     "INJBD",
0038     "INVALIDINO",
0039     "BLOCKECC",
0040     "BLOCKNO",
0041     "VALIDFLAG",
0042     "GENERATION",
0043     "UNSUPPORTED"
0044 };
0045 
0046 struct ocfs2_filecheck_entry {
0047     struct list_head fe_list;
0048     unsigned long fe_ino;
0049     unsigned int fe_type;
0050     unsigned int fe_done:1;
0051     unsigned int fe_status:31;
0052 };
0053 
0054 struct ocfs2_filecheck_args {
0055     unsigned int fa_type;
0056     union {
0057         unsigned long fa_ino;
0058         unsigned int fa_len;
0059     };
0060 };
0061 
0062 static const char *
0063 ocfs2_filecheck_error(int errno)
0064 {
0065     if (!errno)
0066         return ocfs2_filecheck_errs[errno];
0067 
0068     BUG_ON(errno < OCFS2_FILECHECK_ERR_START ||
0069            errno > OCFS2_FILECHECK_ERR_END);
0070     return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1];
0071 }
0072 
0073 static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
0074                     struct kobj_attribute *attr,
0075                     char *buf);
0076 static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
0077                     struct kobj_attribute *attr,
0078                     const char *buf, size_t count);
0079 static struct kobj_attribute ocfs2_filecheck_attr_chk =
0080                     __ATTR(check, S_IRUSR | S_IWUSR,
0081                     ocfs2_filecheck_attr_show,
0082                     ocfs2_filecheck_attr_store);
0083 static struct kobj_attribute ocfs2_filecheck_attr_fix =
0084                     __ATTR(fix, S_IRUSR | S_IWUSR,
0085                     ocfs2_filecheck_attr_show,
0086                     ocfs2_filecheck_attr_store);
0087 static struct kobj_attribute ocfs2_filecheck_attr_set =
0088                     __ATTR(set, S_IRUSR | S_IWUSR,
0089                     ocfs2_filecheck_attr_show,
0090                     ocfs2_filecheck_attr_store);
0091 static struct attribute *ocfs2_filecheck_attrs[] = {
0092     &ocfs2_filecheck_attr_chk.attr,
0093     &ocfs2_filecheck_attr_fix.attr,
0094     &ocfs2_filecheck_attr_set.attr,
0095     NULL
0096 };
0097 ATTRIBUTE_GROUPS(ocfs2_filecheck);
0098 
0099 static void ocfs2_filecheck_release(struct kobject *kobj)
0100 {
0101     struct ocfs2_filecheck_sysfs_entry *entry = container_of(kobj,
0102                 struct ocfs2_filecheck_sysfs_entry, fs_kobj);
0103 
0104     complete(&entry->fs_kobj_unregister);
0105 }
0106 
0107 static ssize_t
0108 ocfs2_filecheck_show(struct kobject *kobj, struct attribute *attr, char *buf)
0109 {
0110     ssize_t ret = -EIO;
0111     struct kobj_attribute *kattr = container_of(attr,
0112                     struct kobj_attribute, attr);
0113 
0114     kobject_get(kobj);
0115     if (kattr->show)
0116         ret = kattr->show(kobj, kattr, buf);
0117     kobject_put(kobj);
0118     return ret;
0119 }
0120 
0121 static ssize_t
0122 ocfs2_filecheck_store(struct kobject *kobj, struct attribute *attr,
0123             const char *buf, size_t count)
0124 {
0125     ssize_t ret = -EIO;
0126     struct kobj_attribute *kattr = container_of(attr,
0127                     struct kobj_attribute, attr);
0128 
0129     kobject_get(kobj);
0130     if (kattr->store)
0131         ret = kattr->store(kobj, kattr, buf, count);
0132     kobject_put(kobj);
0133     return ret;
0134 }
0135 
0136 static const struct sysfs_ops ocfs2_filecheck_ops = {
0137     .show = ocfs2_filecheck_show,
0138     .store = ocfs2_filecheck_store,
0139 };
0140 
0141 static struct kobj_type ocfs2_ktype_filecheck = {
0142     .default_groups = ocfs2_filecheck_groups,
0143     .sysfs_ops = &ocfs2_filecheck_ops,
0144     .release = ocfs2_filecheck_release,
0145 };
0146 
0147 static void
0148 ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry)
0149 {
0150     struct ocfs2_filecheck_entry *p;
0151 
0152     spin_lock(&entry->fs_fcheck->fc_lock);
0153     while (!list_empty(&entry->fs_fcheck->fc_head)) {
0154         p = list_first_entry(&entry->fs_fcheck->fc_head,
0155                      struct ocfs2_filecheck_entry, fe_list);
0156         list_del(&p->fe_list);
0157         BUG_ON(!p->fe_done); /* To free a undone file check entry */
0158         kfree(p);
0159     }
0160     spin_unlock(&entry->fs_fcheck->fc_lock);
0161 
0162     kfree(entry->fs_fcheck);
0163     entry->fs_fcheck = NULL;
0164 }
0165 
0166 int ocfs2_filecheck_create_sysfs(struct ocfs2_super *osb)
0167 {
0168     int ret;
0169     struct ocfs2_filecheck *fcheck;
0170     struct ocfs2_filecheck_sysfs_entry *entry = &osb->osb_fc_ent;
0171 
0172     fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS);
0173     if (!fcheck)
0174         return -ENOMEM;
0175 
0176     INIT_LIST_HEAD(&fcheck->fc_head);
0177     spin_lock_init(&fcheck->fc_lock);
0178     fcheck->fc_max = OCFS2_FILECHECK_MINSIZE;
0179     fcheck->fc_size = 0;
0180     fcheck->fc_done = 0;
0181 
0182     entry->fs_kobj.kset = osb->osb_dev_kset;
0183     init_completion(&entry->fs_kobj_unregister);
0184     ret = kobject_init_and_add(&entry->fs_kobj, &ocfs2_ktype_filecheck,
0185                     NULL, "filecheck");
0186     if (ret) {
0187         kobject_put(&entry->fs_kobj);
0188         kfree(fcheck);
0189         return ret;
0190     }
0191 
0192     entry->fs_fcheck = fcheck;
0193     return 0;
0194 }
0195 
0196 void ocfs2_filecheck_remove_sysfs(struct ocfs2_super *osb)
0197 {
0198     if (!osb->osb_fc_ent.fs_fcheck)
0199         return;
0200 
0201     kobject_del(&osb->osb_fc_ent.fs_kobj);
0202     kobject_put(&osb->osb_fc_ent.fs_kobj);
0203     wait_for_completion(&osb->osb_fc_ent.fs_kobj_unregister);
0204     ocfs2_filecheck_sysfs_free(&osb->osb_fc_ent);
0205 }
0206 
0207 static int
0208 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
0209                   unsigned int count);
0210 static int
0211 ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
0212                unsigned int len)
0213 {
0214     int ret;
0215 
0216     if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
0217         return -EINVAL;
0218 
0219     spin_lock(&ent->fs_fcheck->fc_lock);
0220     if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
0221         mlog(ML_NOTICE,
0222         "Cannot set online file check maximum entry number "
0223         "to %u due to too many pending entries(%u)\n",
0224         len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
0225         ret = -EBUSY;
0226     } else {
0227         if (len < ent->fs_fcheck->fc_size)
0228             BUG_ON(!ocfs2_filecheck_erase_entries(ent,
0229                 ent->fs_fcheck->fc_size - len));
0230 
0231         ent->fs_fcheck->fc_max = len;
0232         ret = 0;
0233     }
0234     spin_unlock(&ent->fs_fcheck->fc_lock);
0235 
0236     return ret;
0237 }
0238 
0239 #define OCFS2_FILECHECK_ARGS_LEN    24
0240 static int
0241 ocfs2_filecheck_args_get_long(const char *buf, size_t count,
0242                   unsigned long *val)
0243 {
0244     char buffer[OCFS2_FILECHECK_ARGS_LEN];
0245 
0246     memcpy(buffer, buf, count);
0247     buffer[count] = '\0';
0248 
0249     if (kstrtoul(buffer, 0, val))
0250         return 1;
0251 
0252     return 0;
0253 }
0254 
0255 static int
0256 ocfs2_filecheck_type_parse(const char *name, unsigned int *type)
0257 {
0258     if (!strncmp(name, "fix", 4))
0259         *type = OCFS2_FILECHECK_TYPE_FIX;
0260     else if (!strncmp(name, "check", 6))
0261         *type = OCFS2_FILECHECK_TYPE_CHK;
0262     else if (!strncmp(name, "set", 4))
0263         *type = OCFS2_FILECHECK_TYPE_SET;
0264     else
0265         return 1;
0266 
0267     return 0;
0268 }
0269 
0270 static int
0271 ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
0272                struct ocfs2_filecheck_args *args)
0273 {
0274     unsigned long val = 0;
0275     unsigned int type;
0276 
0277     /* too short/long args length */
0278     if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
0279         return 1;
0280 
0281     if (ocfs2_filecheck_type_parse(name, &type))
0282         return 1;
0283     if (ocfs2_filecheck_args_get_long(buf, count, &val))
0284         return 1;
0285 
0286     if (val <= 0)
0287         return 1;
0288 
0289     args->fa_type = type;
0290     if (type == OCFS2_FILECHECK_TYPE_SET)
0291         args->fa_len = (unsigned int)val;
0292     else
0293         args->fa_ino = val;
0294 
0295     return 0;
0296 }
0297 
0298 static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
0299                     struct kobj_attribute *attr,
0300                     char *buf)
0301 {
0302 
0303     ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
0304     unsigned int type;
0305     struct ocfs2_filecheck_entry *p;
0306     struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
0307                 struct ocfs2_filecheck_sysfs_entry, fs_kobj);
0308 
0309     if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
0310         return -EINVAL;
0311 
0312     if (type == OCFS2_FILECHECK_TYPE_SET) {
0313         spin_lock(&ent->fs_fcheck->fc_lock);
0314         total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
0315         spin_unlock(&ent->fs_fcheck->fc_lock);
0316         goto exit;
0317     }
0318 
0319     ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
0320     total += ret;
0321     remain -= ret;
0322     spin_lock(&ent->fs_fcheck->fc_lock);
0323     list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
0324         if (p->fe_type != type)
0325             continue;
0326 
0327         ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
0328                    p->fe_ino, p->fe_done,
0329                    ocfs2_filecheck_error(p->fe_status));
0330         if (ret >= remain) {
0331             /* snprintf() didn't fit */
0332             total = -E2BIG;
0333             break;
0334         }
0335         total += ret;
0336         remain -= ret;
0337     }
0338     spin_unlock(&ent->fs_fcheck->fc_lock);
0339 
0340 exit:
0341     return total;
0342 }
0343 
0344 static inline int
0345 ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry *ent,
0346                 unsigned long ino)
0347 {
0348     struct ocfs2_filecheck_entry *p;
0349 
0350     list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
0351         if (!p->fe_done) {
0352             if (p->fe_ino == ino)
0353                 return 1;
0354         }
0355     }
0356 
0357     return 0;
0358 }
0359 
0360 static inline int
0361 ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
0362 {
0363     struct ocfs2_filecheck_entry *p;
0364 
0365     list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
0366         if (p->fe_done) {
0367             list_del(&p->fe_list);
0368             kfree(p);
0369             ent->fs_fcheck->fc_size--;
0370             ent->fs_fcheck->fc_done--;
0371             return 1;
0372         }
0373     }
0374 
0375     return 0;
0376 }
0377 
0378 static int
0379 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
0380                   unsigned int count)
0381 {
0382     unsigned int i = 0;
0383     unsigned int ret = 0;
0384 
0385     while (i++ < count) {
0386         if (ocfs2_filecheck_erase_entry(ent))
0387             ret++;
0388         else
0389             break;
0390     }
0391 
0392     return (ret == count ? 1 : 0);
0393 }
0394 
0395 static void
0396 ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
0397                struct ocfs2_filecheck_entry *entry)
0398 {
0399     spin_lock(&ent->fs_fcheck->fc_lock);
0400     entry->fe_done = 1;
0401     ent->fs_fcheck->fc_done++;
0402     spin_unlock(&ent->fs_fcheck->fc_lock);
0403 }
0404 
0405 static unsigned int
0406 ocfs2_filecheck_handle(struct ocfs2_super *osb,
0407                unsigned long ino, unsigned int flags)
0408 {
0409     unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
0410     struct inode *inode = NULL;
0411     int rc;
0412 
0413     inode = ocfs2_iget(osb, ino, flags, 0);
0414     if (IS_ERR(inode)) {
0415         rc = (int)(-(long)inode);
0416         if (rc >= OCFS2_FILECHECK_ERR_START &&
0417             rc < OCFS2_FILECHECK_ERR_END)
0418             ret = rc;
0419         else
0420             ret = OCFS2_FILECHECK_ERR_FAILED;
0421     } else
0422         iput(inode);
0423 
0424     return ret;
0425 }
0426 
0427 static void
0428 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
0429                  struct ocfs2_filecheck_entry *entry)
0430 {
0431     struct ocfs2_super *osb = container_of(ent, struct ocfs2_super,
0432                         osb_fc_ent);
0433 
0434     if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
0435         entry->fe_status = ocfs2_filecheck_handle(osb,
0436                 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
0437     else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
0438         entry->fe_status = ocfs2_filecheck_handle(osb,
0439                 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
0440     else
0441         entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
0442 
0443     ocfs2_filecheck_done_entry(ent, entry);
0444 }
0445 
0446 static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
0447                      struct kobj_attribute *attr,
0448                      const char *buf, size_t count)
0449 {
0450     ssize_t ret = 0;
0451     struct ocfs2_filecheck_args args;
0452     struct ocfs2_filecheck_entry *entry;
0453     struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
0454                 struct ocfs2_filecheck_sysfs_entry, fs_kobj);
0455 
0456     if (count == 0)
0457         return count;
0458 
0459     if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args))
0460         return -EINVAL;
0461 
0462     if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
0463         ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
0464         goto exit;
0465     }
0466 
0467     entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
0468     if (!entry) {
0469         ret = -ENOMEM;
0470         goto exit;
0471     }
0472 
0473     spin_lock(&ent->fs_fcheck->fc_lock);
0474     if (ocfs2_filecheck_is_dup_entry(ent, args.fa_ino)) {
0475         ret = -EEXIST;
0476         kfree(entry);
0477     } else if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
0478         (ent->fs_fcheck->fc_done == 0)) {
0479         mlog(ML_NOTICE,
0480         "Cannot do more file check "
0481         "since file check queue(%u) is full now\n",
0482         ent->fs_fcheck->fc_max);
0483         ret = -EAGAIN;
0484         kfree(entry);
0485     } else {
0486         if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
0487             (ent->fs_fcheck->fc_done > 0)) {
0488             /* Delete the oldest entry which was done,
0489              * make sure the entry size in list does
0490              * not exceed maximum value
0491              */
0492             BUG_ON(!ocfs2_filecheck_erase_entry(ent));
0493         }
0494 
0495         entry->fe_ino = args.fa_ino;
0496         entry->fe_type = args.fa_type;
0497         entry->fe_done = 0;
0498         entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
0499         list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
0500         ent->fs_fcheck->fc_size++;
0501     }
0502     spin_unlock(&ent->fs_fcheck->fc_lock);
0503 
0504     if (!ret)
0505         ocfs2_filecheck_handle_entry(ent, entry);
0506 
0507 exit:
0508     return (!ret ? count : ret);
0509 }