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}