1 /* -*- mode: c; c-basic-offset: 8; -*- 2 * vim: noexpandtab sw=8 ts=8 sts=0: 3 * 4 * filecheck.c 5 * 6 * Code which implements online file check. 7 * 8 * Copyright (C) 2016 SuSE. All rights reserved. 9 * 10 * This program is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU General Public 12 * License as published by the Free Software Foundation, version 2. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 */ 19 20 #include <linux/list.h> 21 #include <linux/spinlock.h> 22 #include <linux/module.h> 23 #include <linux/slab.h> 24 #include <linux/kmod.h> 25 #include <linux/fs.h> 26 #include <linux/kobject.h> 27 #include <linux/sysfs.h> 28 #include <linux/sysctl.h> 29 #include <cluster/masklog.h> 30 31 #include "ocfs2.h" 32 #include "ocfs2_fs.h" 33 #include "stackglue.h" 34 #include "inode.h" 35 36 #include "filecheck.h" 37 38 39 /* File check error strings, 40 * must correspond with error number in header file. 41 */ 42 static const char * const ocfs2_filecheck_errs[] = { 43 "SUCCESS", 44 "FAILED", 45 "INPROGRESS", 46 "READONLY", 47 "INJBD", 48 "INVALIDINO", 49 "BLOCKECC", 50 "BLOCKNO", 51 "VALIDFLAG", 52 "GENERATION", 53 "UNSUPPORTED" 54 }; 55 56 struct ocfs2_filecheck_entry { 57 struct list_head fe_list; 58 unsigned long fe_ino; 59 unsigned int fe_type; 60 unsigned int fe_done:1; 61 unsigned int fe_status:31; 62 }; 63 64 struct ocfs2_filecheck_args { 65 unsigned int fa_type; 66 union { 67 unsigned long fa_ino; 68 unsigned int fa_len; 69 }; 70 }; 71 72 static const char * 73 ocfs2_filecheck_error(int errno) 74 { 75 if (!errno) 76 return ocfs2_filecheck_errs[errno]; 77 78 BUG_ON(errno < OCFS2_FILECHECK_ERR_START || 79 errno > OCFS2_FILECHECK_ERR_END); 80 return ocfs2_filecheck_errs[errno - OCFS2_FILECHECK_ERR_START + 1]; 81 } 82 83 static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj, 84 struct kobj_attribute *attr, 85 char *buf); 86 static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj, 87 struct kobj_attribute *attr, 88 const char *buf, size_t count); 89 static struct kobj_attribute ocfs2_filecheck_attr_chk = 90 __ATTR(check, S_IRUSR | S_IWUSR, 91 ocfs2_filecheck_attr_show, 92 ocfs2_filecheck_attr_store); 93 static struct kobj_attribute ocfs2_filecheck_attr_fix = 94 __ATTR(fix, S_IRUSR | S_IWUSR, 95 ocfs2_filecheck_attr_show, 96 ocfs2_filecheck_attr_store); 97 static struct kobj_attribute ocfs2_filecheck_attr_set = 98 __ATTR(set, S_IRUSR | S_IWUSR, 99 ocfs2_filecheck_attr_show, 100 ocfs2_filecheck_attr_store); 101 static struct attribute *ocfs2_filecheck_attrs[] = { 102 &ocfs2_filecheck_attr_chk.attr, 103 &ocfs2_filecheck_attr_fix.attr, 104 &ocfs2_filecheck_attr_set.attr, 105 NULL 106 }; 107 108 static void ocfs2_filecheck_release(struct kobject *kobj) 109 { 110 struct ocfs2_filecheck_sysfs_entry *entry = container_of(kobj, 111 struct ocfs2_filecheck_sysfs_entry, fs_kobj); 112 113 complete(&entry->fs_kobj_unregister); 114 } 115 116 static ssize_t 117 ocfs2_filecheck_show(struct kobject *kobj, struct attribute *attr, char *buf) 118 { 119 ssize_t ret = -EIO; 120 struct kobj_attribute *kattr = container_of(attr, 121 struct kobj_attribute, attr); 122 123 kobject_get(kobj); 124 if (kattr->show) 125 ret = kattr->show(kobj, kattr, buf); 126 kobject_put(kobj); 127 return ret; 128 } 129 130 static ssize_t 131 ocfs2_filecheck_store(struct kobject *kobj, struct attribute *attr, 132 const char *buf, size_t count) 133 { 134 ssize_t ret = -EIO; 135 struct kobj_attribute *kattr = container_of(attr, 136 struct kobj_attribute, attr); 137 138 kobject_get(kobj); 139 if (kattr->store) 140 ret = kattr->store(kobj, kattr, buf, count); 141 kobject_put(kobj); 142 return ret; 143 } 144 145 static const struct sysfs_ops ocfs2_filecheck_ops = { 146 .show = ocfs2_filecheck_show, 147 .store = ocfs2_filecheck_store, 148 }; 149 150 static struct kobj_type ocfs2_ktype_filecheck = { 151 .default_attrs = ocfs2_filecheck_attrs, 152 .sysfs_ops = &ocfs2_filecheck_ops, 153 .release = ocfs2_filecheck_release, 154 }; 155 156 static void 157 ocfs2_filecheck_sysfs_free(struct ocfs2_filecheck_sysfs_entry *entry) 158 { 159 struct ocfs2_filecheck_entry *p; 160 161 spin_lock(&entry->fs_fcheck->fc_lock); 162 while (!list_empty(&entry->fs_fcheck->fc_head)) { 163 p = list_first_entry(&entry->fs_fcheck->fc_head, 164 struct ocfs2_filecheck_entry, fe_list); 165 list_del(&p->fe_list); 166 BUG_ON(!p->fe_done); /* To free a undone file check entry */ 167 kfree(p); 168 } 169 spin_unlock(&entry->fs_fcheck->fc_lock); 170 171 kfree(entry->fs_fcheck); 172 entry->fs_fcheck = NULL; 173 } 174 175 int ocfs2_filecheck_create_sysfs(struct ocfs2_super *osb) 176 { 177 int ret; 178 struct ocfs2_filecheck *fcheck; 179 struct ocfs2_filecheck_sysfs_entry *entry = &osb->osb_fc_ent; 180 181 fcheck = kmalloc(sizeof(struct ocfs2_filecheck), GFP_NOFS); 182 if (!fcheck) 183 return -ENOMEM; 184 185 INIT_LIST_HEAD(&fcheck->fc_head); 186 spin_lock_init(&fcheck->fc_lock); 187 fcheck->fc_max = OCFS2_FILECHECK_MINSIZE; 188 fcheck->fc_size = 0; 189 fcheck->fc_done = 0; 190 191 entry->fs_kobj.kset = osb->osb_dev_kset; 192 init_completion(&entry->fs_kobj_unregister); 193 ret = kobject_init_and_add(&entry->fs_kobj, &ocfs2_ktype_filecheck, 194 NULL, "filecheck"); 195 if (ret) { 196 kobject_put(&entry->fs_kobj); 197 kfree(fcheck); 198 return ret; 199 } 200 201 entry->fs_fcheck = fcheck; 202 return 0; 203 } 204 205 void ocfs2_filecheck_remove_sysfs(struct ocfs2_super *osb) 206 { 207 if (!osb->osb_fc_ent.fs_fcheck) 208 return; 209 210 kobject_del(&osb->osb_fc_ent.fs_kobj); 211 kobject_put(&osb->osb_fc_ent.fs_kobj); 212 wait_for_completion(&osb->osb_fc_ent.fs_kobj_unregister); 213 ocfs2_filecheck_sysfs_free(&osb->osb_fc_ent); 214 } 215 216 static int 217 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent, 218 unsigned int count); 219 static int 220 ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent, 221 unsigned int len) 222 { 223 int ret; 224 225 if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE)) 226 return -EINVAL; 227 228 spin_lock(&ent->fs_fcheck->fc_lock); 229 if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) { 230 mlog(ML_NOTICE, 231 "Cannot set online file check maximum entry number " 232 "to %u due to too many pending entries(%u)\n", 233 len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done); 234 ret = -EBUSY; 235 } else { 236 if (len < ent->fs_fcheck->fc_size) 237 BUG_ON(!ocfs2_filecheck_erase_entries(ent, 238 ent->fs_fcheck->fc_size - len)); 239 240 ent->fs_fcheck->fc_max = len; 241 ret = 0; 242 } 243 spin_unlock(&ent->fs_fcheck->fc_lock); 244 245 return ret; 246 } 247 248 #define OCFS2_FILECHECK_ARGS_LEN 24 249 static int 250 ocfs2_filecheck_args_get_long(const char *buf, size_t count, 251 unsigned long *val) 252 { 253 char buffer[OCFS2_FILECHECK_ARGS_LEN]; 254 255 memcpy(buffer, buf, count); 256 buffer[count] = '\0'; 257 258 if (kstrtoul(buffer, 0, val)) 259 return 1; 260 261 return 0; 262 } 263 264 static int 265 ocfs2_filecheck_type_parse(const char *name, unsigned int *type) 266 { 267 if (!strncmp(name, "fix", 4)) 268 *type = OCFS2_FILECHECK_TYPE_FIX; 269 else if (!strncmp(name, "check", 6)) 270 *type = OCFS2_FILECHECK_TYPE_CHK; 271 else if (!strncmp(name, "set", 4)) 272 *type = OCFS2_FILECHECK_TYPE_SET; 273 else 274 return 1; 275 276 return 0; 277 } 278 279 static int 280 ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count, 281 struct ocfs2_filecheck_args *args) 282 { 283 unsigned long val = 0; 284 unsigned int type; 285 286 /* too short/long args length */ 287 if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN)) 288 return 1; 289 290 if (ocfs2_filecheck_type_parse(name, &type)) 291 return 1; 292 if (ocfs2_filecheck_args_get_long(buf, count, &val)) 293 return 1; 294 295 if (val <= 0) 296 return 1; 297 298 args->fa_type = type; 299 if (type == OCFS2_FILECHECK_TYPE_SET) 300 args->fa_len = (unsigned int)val; 301 else 302 args->fa_ino = val; 303 304 return 0; 305 } 306 307 static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj, 308 struct kobj_attribute *attr, 309 char *buf) 310 { 311 312 ssize_t ret = 0, total = 0, remain = PAGE_SIZE; 313 unsigned int type; 314 struct ocfs2_filecheck_entry *p; 315 struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj, 316 struct ocfs2_filecheck_sysfs_entry, fs_kobj); 317 318 if (ocfs2_filecheck_type_parse(attr->attr.name, &type)) 319 return -EINVAL; 320 321 if (type == OCFS2_FILECHECK_TYPE_SET) { 322 spin_lock(&ent->fs_fcheck->fc_lock); 323 total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max); 324 spin_unlock(&ent->fs_fcheck->fc_lock); 325 goto exit; 326 } 327 328 ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n"); 329 total += ret; 330 remain -= ret; 331 spin_lock(&ent->fs_fcheck->fc_lock); 332 list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { 333 if (p->fe_type != type) 334 continue; 335 336 ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n", 337 p->fe_ino, p->fe_done, 338 ocfs2_filecheck_error(p->fe_status)); 339 if (ret < 0) { 340 total = ret; 341 break; 342 } 343 if (ret == remain) { 344 /* snprintf() didn't fit */ 345 total = -E2BIG; 346 break; 347 } 348 total += ret; 349 remain -= ret; 350 } 351 spin_unlock(&ent->fs_fcheck->fc_lock); 352 353 exit: 354 return total; 355 } 356 357 static inline int 358 ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry *ent, 359 unsigned long ino) 360 { 361 struct ocfs2_filecheck_entry *p; 362 363 list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { 364 if (!p->fe_done) { 365 if (p->fe_ino == ino) 366 return 1; 367 } 368 } 369 370 return 0; 371 } 372 373 static inline int 374 ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent) 375 { 376 struct ocfs2_filecheck_entry *p; 377 378 list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) { 379 if (p->fe_done) { 380 list_del(&p->fe_list); 381 kfree(p); 382 ent->fs_fcheck->fc_size--; 383 ent->fs_fcheck->fc_done--; 384 return 1; 385 } 386 } 387 388 return 0; 389 } 390 391 static int 392 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent, 393 unsigned int count) 394 { 395 unsigned int i = 0; 396 unsigned int ret = 0; 397 398 while (i++ < count) { 399 if (ocfs2_filecheck_erase_entry(ent)) 400 ret++; 401 else 402 break; 403 } 404 405 return (ret == count ? 1 : 0); 406 } 407 408 static void 409 ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent, 410 struct ocfs2_filecheck_entry *entry) 411 { 412 spin_lock(&ent->fs_fcheck->fc_lock); 413 entry->fe_done = 1; 414 ent->fs_fcheck->fc_done++; 415 spin_unlock(&ent->fs_fcheck->fc_lock); 416 } 417 418 static unsigned int 419 ocfs2_filecheck_handle(struct ocfs2_super *osb, 420 unsigned long ino, unsigned int flags) 421 { 422 unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS; 423 struct inode *inode = NULL; 424 int rc; 425 426 inode = ocfs2_iget(osb, ino, flags, 0); 427 if (IS_ERR(inode)) { 428 rc = (int)(-(long)inode); 429 if (rc >= OCFS2_FILECHECK_ERR_START && 430 rc < OCFS2_FILECHECK_ERR_END) 431 ret = rc; 432 else 433 ret = OCFS2_FILECHECK_ERR_FAILED; 434 } else 435 iput(inode); 436 437 return ret; 438 } 439 440 static void 441 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent, 442 struct ocfs2_filecheck_entry *entry) 443 { 444 struct ocfs2_super *osb = container_of(ent, struct ocfs2_super, 445 osb_fc_ent); 446 447 if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK) 448 entry->fe_status = ocfs2_filecheck_handle(osb, 449 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK); 450 else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX) 451 entry->fe_status = ocfs2_filecheck_handle(osb, 452 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX); 453 else 454 entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED; 455 456 ocfs2_filecheck_done_entry(ent, entry); 457 } 458 459 static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj, 460 struct kobj_attribute *attr, 461 const char *buf, size_t count) 462 { 463 ssize_t ret = 0; 464 struct ocfs2_filecheck_args args; 465 struct ocfs2_filecheck_entry *entry; 466 struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj, 467 struct ocfs2_filecheck_sysfs_entry, fs_kobj); 468 469 if (count == 0) 470 return count; 471 472 if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args)) 473 return -EINVAL; 474 475 if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) { 476 ret = ocfs2_filecheck_adjust_max(ent, args.fa_len); 477 goto exit; 478 } 479 480 entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS); 481 if (!entry) { 482 ret = -ENOMEM; 483 goto exit; 484 } 485 486 spin_lock(&ent->fs_fcheck->fc_lock); 487 if (ocfs2_filecheck_is_dup_entry(ent, args.fa_ino)) { 488 ret = -EEXIST; 489 kfree(entry); 490 } else if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && 491 (ent->fs_fcheck->fc_done == 0)) { 492 mlog(ML_NOTICE, 493 "Cannot do more file check " 494 "since file check queue(%u) is full now\n", 495 ent->fs_fcheck->fc_max); 496 ret = -EAGAIN; 497 kfree(entry); 498 } else { 499 if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) && 500 (ent->fs_fcheck->fc_done > 0)) { 501 /* Delete the oldest entry which was done, 502 * make sure the entry size in list does 503 * not exceed maximum value 504 */ 505 BUG_ON(!ocfs2_filecheck_erase_entry(ent)); 506 } 507 508 entry->fe_ino = args.fa_ino; 509 entry->fe_type = args.fa_type; 510 entry->fe_done = 0; 511 entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS; 512 list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head); 513 ent->fs_fcheck->fc_size++; 514 } 515 spin_unlock(&ent->fs_fcheck->fc_lock); 516 517 if (!ret) 518 ocfs2_filecheck_handle_entry(ent, entry); 519 520 exit: 521 return (!ret ? count : ret); 522 } 523