~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/ocfs2/filecheck.c

Version: ~ [ linux-5.2-rc1 ] ~ [ linux-5.1.2 ] ~ [ linux-5.0.16 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.43 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.119 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.176 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.179 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.139 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.67 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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                 kfree(fcheck);
197                 return ret;
198         }
199 
200         entry->fs_fcheck = fcheck;
201         return 0;
202 }
203 
204 void ocfs2_filecheck_remove_sysfs(struct ocfs2_super *osb)
205 {
206         if (!osb->osb_fc_ent.fs_fcheck)
207                 return;
208 
209         kobject_del(&osb->osb_fc_ent.fs_kobj);
210         kobject_put(&osb->osb_fc_ent.fs_kobj);
211         wait_for_completion(&osb->osb_fc_ent.fs_kobj_unregister);
212         ocfs2_filecheck_sysfs_free(&osb->osb_fc_ent);
213 }
214 
215 static int
216 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
217                               unsigned int count);
218 static int
219 ocfs2_filecheck_adjust_max(struct ocfs2_filecheck_sysfs_entry *ent,
220                            unsigned int len)
221 {
222         int ret;
223 
224         if ((len < OCFS2_FILECHECK_MINSIZE) || (len > OCFS2_FILECHECK_MAXSIZE))
225                 return -EINVAL;
226 
227         spin_lock(&ent->fs_fcheck->fc_lock);
228         if (len < (ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done)) {
229                 mlog(ML_NOTICE,
230                 "Cannot set online file check maximum entry number "
231                 "to %u due to too many pending entries(%u)\n",
232                 len, ent->fs_fcheck->fc_size - ent->fs_fcheck->fc_done);
233                 ret = -EBUSY;
234         } else {
235                 if (len < ent->fs_fcheck->fc_size)
236                         BUG_ON(!ocfs2_filecheck_erase_entries(ent,
237                                 ent->fs_fcheck->fc_size - len));
238 
239                 ent->fs_fcheck->fc_max = len;
240                 ret = 0;
241         }
242         spin_unlock(&ent->fs_fcheck->fc_lock);
243 
244         return ret;
245 }
246 
247 #define OCFS2_FILECHECK_ARGS_LEN        24
248 static int
249 ocfs2_filecheck_args_get_long(const char *buf, size_t count,
250                               unsigned long *val)
251 {
252         char buffer[OCFS2_FILECHECK_ARGS_LEN];
253 
254         memcpy(buffer, buf, count);
255         buffer[count] = '\0';
256 
257         if (kstrtoul(buffer, 0, val))
258                 return 1;
259 
260         return 0;
261 }
262 
263 static int
264 ocfs2_filecheck_type_parse(const char *name, unsigned int *type)
265 {
266         if (!strncmp(name, "fix", 4))
267                 *type = OCFS2_FILECHECK_TYPE_FIX;
268         else if (!strncmp(name, "check", 6))
269                 *type = OCFS2_FILECHECK_TYPE_CHK;
270         else if (!strncmp(name, "set", 4))
271                 *type = OCFS2_FILECHECK_TYPE_SET;
272         else
273                 return 1;
274 
275         return 0;
276 }
277 
278 static int
279 ocfs2_filecheck_args_parse(const char *name, const char *buf, size_t count,
280                            struct ocfs2_filecheck_args *args)
281 {
282         unsigned long val = 0;
283         unsigned int type;
284 
285         /* too short/long args length */
286         if ((count < 1) || (count >= OCFS2_FILECHECK_ARGS_LEN))
287                 return 1;
288 
289         if (ocfs2_filecheck_type_parse(name, &type))
290                 return 1;
291         if (ocfs2_filecheck_args_get_long(buf, count, &val))
292                 return 1;
293 
294         if (val <= 0)
295                 return 1;
296 
297         args->fa_type = type;
298         if (type == OCFS2_FILECHECK_TYPE_SET)
299                 args->fa_len = (unsigned int)val;
300         else
301                 args->fa_ino = val;
302 
303         return 0;
304 }
305 
306 static ssize_t ocfs2_filecheck_attr_show(struct kobject *kobj,
307                                     struct kobj_attribute *attr,
308                                     char *buf)
309 {
310 
311         ssize_t ret = 0, total = 0, remain = PAGE_SIZE;
312         unsigned int type;
313         struct ocfs2_filecheck_entry *p;
314         struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
315                                 struct ocfs2_filecheck_sysfs_entry, fs_kobj);
316 
317         if (ocfs2_filecheck_type_parse(attr->attr.name, &type))
318                 return -EINVAL;
319 
320         if (type == OCFS2_FILECHECK_TYPE_SET) {
321                 spin_lock(&ent->fs_fcheck->fc_lock);
322                 total = snprintf(buf, remain, "%u\n", ent->fs_fcheck->fc_max);
323                 spin_unlock(&ent->fs_fcheck->fc_lock);
324                 goto exit;
325         }
326 
327         ret = snprintf(buf, remain, "INO\t\tDONE\tERROR\n");
328         total += ret;
329         remain -= ret;
330         spin_lock(&ent->fs_fcheck->fc_lock);
331         list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
332                 if (p->fe_type != type)
333                         continue;
334 
335                 ret = snprintf(buf + total, remain, "%lu\t\t%u\t%s\n",
336                                p->fe_ino, p->fe_done,
337                                ocfs2_filecheck_error(p->fe_status));
338                 if (ret < 0) {
339                         total = ret;
340                         break;
341                 }
342                 if (ret == remain) {
343                         /* snprintf() didn't fit */
344                         total = -E2BIG;
345                         break;
346                 }
347                 total += ret;
348                 remain -= ret;
349         }
350         spin_unlock(&ent->fs_fcheck->fc_lock);
351 
352 exit:
353         return total;
354 }
355 
356 static inline int
357 ocfs2_filecheck_is_dup_entry(struct ocfs2_filecheck_sysfs_entry *ent,
358                                 unsigned long ino)
359 {
360         struct ocfs2_filecheck_entry *p;
361 
362         list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
363                 if (!p->fe_done) {
364                         if (p->fe_ino == ino)
365                                 return 1;
366                 }
367         }
368 
369         return 0;
370 }
371 
372 static inline int
373 ocfs2_filecheck_erase_entry(struct ocfs2_filecheck_sysfs_entry *ent)
374 {
375         struct ocfs2_filecheck_entry *p;
376 
377         list_for_each_entry(p, &ent->fs_fcheck->fc_head, fe_list) {
378                 if (p->fe_done) {
379                         list_del(&p->fe_list);
380                         kfree(p);
381                         ent->fs_fcheck->fc_size--;
382                         ent->fs_fcheck->fc_done--;
383                         return 1;
384                 }
385         }
386 
387         return 0;
388 }
389 
390 static int
391 ocfs2_filecheck_erase_entries(struct ocfs2_filecheck_sysfs_entry *ent,
392                               unsigned int count)
393 {
394         unsigned int i = 0;
395         unsigned int ret = 0;
396 
397         while (i++ < count) {
398                 if (ocfs2_filecheck_erase_entry(ent))
399                         ret++;
400                 else
401                         break;
402         }
403 
404         return (ret == count ? 1 : 0);
405 }
406 
407 static void
408 ocfs2_filecheck_done_entry(struct ocfs2_filecheck_sysfs_entry *ent,
409                            struct ocfs2_filecheck_entry *entry)
410 {
411         spin_lock(&ent->fs_fcheck->fc_lock);
412         entry->fe_done = 1;
413         ent->fs_fcheck->fc_done++;
414         spin_unlock(&ent->fs_fcheck->fc_lock);
415 }
416 
417 static unsigned int
418 ocfs2_filecheck_handle(struct ocfs2_super *osb,
419                        unsigned long ino, unsigned int flags)
420 {
421         unsigned int ret = OCFS2_FILECHECK_ERR_SUCCESS;
422         struct inode *inode = NULL;
423         int rc;
424 
425         inode = ocfs2_iget(osb, ino, flags, 0);
426         if (IS_ERR(inode)) {
427                 rc = (int)(-(long)inode);
428                 if (rc >= OCFS2_FILECHECK_ERR_START &&
429                     rc < OCFS2_FILECHECK_ERR_END)
430                         ret = rc;
431                 else
432                         ret = OCFS2_FILECHECK_ERR_FAILED;
433         } else
434                 iput(inode);
435 
436         return ret;
437 }
438 
439 static void
440 ocfs2_filecheck_handle_entry(struct ocfs2_filecheck_sysfs_entry *ent,
441                              struct ocfs2_filecheck_entry *entry)
442 {
443         struct ocfs2_super *osb = container_of(ent, struct ocfs2_super,
444                                                 osb_fc_ent);
445 
446         if (entry->fe_type == OCFS2_FILECHECK_TYPE_CHK)
447                 entry->fe_status = ocfs2_filecheck_handle(osb,
448                                 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_CHK);
449         else if (entry->fe_type == OCFS2_FILECHECK_TYPE_FIX)
450                 entry->fe_status = ocfs2_filecheck_handle(osb,
451                                 entry->fe_ino, OCFS2_FI_FLAG_FILECHECK_FIX);
452         else
453                 entry->fe_status = OCFS2_FILECHECK_ERR_UNSUPPORTED;
454 
455         ocfs2_filecheck_done_entry(ent, entry);
456 }
457 
458 static ssize_t ocfs2_filecheck_attr_store(struct kobject *kobj,
459                                      struct kobj_attribute *attr,
460                                      const char *buf, size_t count)
461 {
462         ssize_t ret = 0;
463         struct ocfs2_filecheck_args args;
464         struct ocfs2_filecheck_entry *entry;
465         struct ocfs2_filecheck_sysfs_entry *ent = container_of(kobj,
466                                 struct ocfs2_filecheck_sysfs_entry, fs_kobj);
467 
468         if (count == 0)
469                 return count;
470 
471         if (ocfs2_filecheck_args_parse(attr->attr.name, buf, count, &args))
472                 return -EINVAL;
473 
474         if (args.fa_type == OCFS2_FILECHECK_TYPE_SET) {
475                 ret = ocfs2_filecheck_adjust_max(ent, args.fa_len);
476                 goto exit;
477         }
478 
479         entry = kmalloc(sizeof(struct ocfs2_filecheck_entry), GFP_NOFS);
480         if (!entry) {
481                 ret = -ENOMEM;
482                 goto exit;
483         }
484 
485         spin_lock(&ent->fs_fcheck->fc_lock);
486         if (ocfs2_filecheck_is_dup_entry(ent, args.fa_ino)) {
487                 ret = -EEXIST;
488                 kfree(entry);
489         } else if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
490                 (ent->fs_fcheck->fc_done == 0)) {
491                 mlog(ML_NOTICE,
492                 "Cannot do more file check "
493                 "since file check queue(%u) is full now\n",
494                 ent->fs_fcheck->fc_max);
495                 ret = -EAGAIN;
496                 kfree(entry);
497         } else {
498                 if ((ent->fs_fcheck->fc_size >= ent->fs_fcheck->fc_max) &&
499                     (ent->fs_fcheck->fc_done > 0)) {
500                         /* Delete the oldest entry which was done,
501                          * make sure the entry size in list does
502                          * not exceed maximum value
503                          */
504                         BUG_ON(!ocfs2_filecheck_erase_entry(ent));
505                 }
506 
507                 entry->fe_ino = args.fa_ino;
508                 entry->fe_type = args.fa_type;
509                 entry->fe_done = 0;
510                 entry->fe_status = OCFS2_FILECHECK_ERR_INPROGRESS;
511                 list_add_tail(&entry->fe_list, &ent->fs_fcheck->fc_head);
512                 ent->fs_fcheck->fc_size++;
513         }
514         spin_unlock(&ent->fs_fcheck->fc_lock);
515 
516         if (!ret)
517                 ocfs2_filecheck_handle_entry(ent, entry);
518 
519 exit:
520         return (!ret ? count : ret);
521 }
522 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp