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