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