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 >= remain) { 332 /* snprintf() didn't fit */ 333 total = -E2BIG; 334 break; 335 } 336 total += ret; 337 remain -= ret; 338 } 339 spin_unlock(&ent->fs_fcheck->fc_lock); 340 341exit: 342 return total; 343} 344 345static inline int 346ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry *ent, 347 unsigned long ino) 348{ 349 struct ocfs2_filecheck_entry *p; 350 351 list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { 352 if (!p->fe_done) { 353 if (p->fe_ino == ino) 354 return 1; 355 } 356 } 357 358 return 0; 359} 360 361static inline int 362ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent) 363{ 364 struct ocfs2_filecheck_entry *p; 365 366 list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { 367 if (p->fe_done) { 368 list_del(&p->fe_list); 369 kfree(p); 370 ent->fs_fcheck->fc_size--; 371 ent->fs_fcheck->fc_done--; 372 return 1; 373 } 374 } 375 376 return 0; 377} 378 379static int 380ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent, 381 unsigned int count) 382{ 383 unsigned int i = 0; 384 unsigned int ret = 0; 385 386 while (i++ < count) { 387 if (ocfs2_filecheck_erase_entry(ent)) 388 ret++; 389 else 390 break; 391 } 392 393 return (ret == count ? 1 : 0); 394} 395 396static void 397ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent, 398 struct ocfs2_filecheck_entry *entry) 399{ 400 spin_lock(&ent->fs_fcheck->fc_lock); 401 entry->fe_done = 1; 402 ent->fs_fcheck->fc_done++; 403 spin_unlock(&ent->fs_fcheck->fc_lock); 404} 405 406static unsigned int 407ocfs2_filecheck_handle(struct ocfs2_super *osb, 408 unsigned long ino, unsigned int flags) 409{ 410 unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS; 411 struct inode *inode = NULL; 412 int rc; 413 414 inode = ocfs2_iget(osb, ino, flags, 0); 415 if (IS_ERR(inode)) { 416 rc = (int)(-(long)inode); 417 if (rc >= OCFS2_FILECHECK_ERR_START && 418 rc < OCFS2_FILECHECK_ERR_END) 419 ret = rc; 420 else 421 ret = OCFS2_FILECHECK_ERR_FAILED; 422 } else 423 iput(inode); 424 425 return ret; 426} 427 428static void 429ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent, 430 struct ocfs2_filecheck_entry *entry) 431{ 432 struct ocfs2_super *osb = container_of(ent, struct ocfs2_super, 433 osb_fc_ent); 434 435 if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK) 436 entry->fe_status = ocfs2_filecheck_handle(osb, 437 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK); 438 else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX) 439 entry->fe_status = ocfs2_filecheck_handle(osb, 440 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX); 441 else 442 entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED; 443 444 ocfs2_filecheck_done_entry(ent, entry); 445} 446 447static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj, 448 struct kobj_attribute *attr, 449 const char *buf, size_t count) 450{ 451 ssize_t ret = 0; 452 struct ocfs2_filecheck_args args; 453 struct ocfs2_filecheck_entry *entry; 454 struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj, 455 struct ocfs2_filecheck_sysfs_entry, fs_kobj); 456 457 if (count == 0) 458 return count; 459 460 if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args)) 461 return -EINVAL; 462 463 if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) { 464 ret = ocfs2_filecheck_adjust_max(ent, args.fa_len); 465 goto exit; 466 } 467 468 entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS); 469 if (!entry) { 470 ret = -ENOMEM; 471 goto exit; 472 } 473 474 spin_lock(&ent->fs_fcheck->fc_lock); 475 if (ocfs2_filecheck_is_dup_entry(ent, args.fa_ino)) { 476 ret = -EEXIST; 477 kfree(entry); 478 } else if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && 479 (ent->fs_fcheck->fc_done == 0)) { 480 mlog(ML_NOTICE, 481 "Cannot do more file check " 482 "since file check queue(%u) is full now\n", 483 ent->fs_fcheck->fc_max); 484 ret = -EAGAIN; 485 kfree(entry); 486 } else { 487 if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && 488 (ent->fs_fcheck->fc_done > 0)) { 489 /* Delete the oldest entry which was done, 490 * make sure the entry size in list does 491 * not exceed maximum value 492 */ 493 BUG_ON(!ocfs2_filecheck_erase_entry(ent)); 494 } 495 496 entry->fe_ino = args.fa_ino; 497 entry->fe_type = args.fa_type; 498 entry->fe_done = 0; 499 entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS; 500 list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head); 501 ent->fs_fcheck->fc_size++; 502 } 503 spin_unlock(&ent->fs_fcheck->fc_lock); 504 505 if (!ret) 506 ocfs2_filecheck_handle_entry(ent, entry); 507 508exit: 509 return (!ret ? count : ret); 510} 511