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