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

TOMOYO Linux Cross Reference
Linux/fs/xfs/scrub/dirtree_repair.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  * Copyright (c) 2023-2024 Oracle.  All Rights Reserved.
  4  * Author: Darrick J. Wong <djwong@kernel.org>
  5  */
  6 #include "xfs.h"
  7 #include "xfs_fs.h"
  8 #include "xfs_shared.h"
  9 #include "xfs_format.h"
 10 #include "xfs_trans_resv.h"
 11 #include "xfs_trans_space.h"
 12 #include "xfs_mount.h"
 13 #include "xfs_log_format.h"
 14 #include "xfs_trans.h"
 15 #include "xfs_inode.h"
 16 #include "xfs_icache.h"
 17 #include "xfs_dir2.h"
 18 #include "xfs_dir2_priv.h"
 19 #include "xfs_attr.h"
 20 #include "xfs_parent.h"
 21 #include "scrub/scrub.h"
 22 #include "scrub/common.h"
 23 #include "scrub/bitmap.h"
 24 #include "scrub/ino_bitmap.h"
 25 #include "scrub/xfile.h"
 26 #include "scrub/xfarray.h"
 27 #include "scrub/xfblob.h"
 28 #include "scrub/listxattr.h"
 29 #include "scrub/trace.h"
 30 #include "scrub/repair.h"
 31 #include "scrub/orphanage.h"
 32 #include "scrub/dirtree.h"
 33 #include "scrub/readdir.h"
 34 
 35 /*
 36  * Directory Tree Structure Repairs
 37  * ================================
 38  *
 39  * If we decide that the directory being scanned is participating in a
 40  * directory loop, the only change we can make is to remove directory entries
 41  * pointing down to @sc->ip.  If that leaves it with no parents, the directory
 42  * should be adopted by the orphanage.
 43  */
 44 
 45 /* Set up to repair directory loops. */
 46 int
 47 xrep_setup_dirtree(
 48         struct xfs_scrub        *sc)
 49 {
 50         return xrep_orphanage_try_create(sc);
 51 }
 52 
 53 /* Change the outcome of this path. */
 54 static inline void
 55 xrep_dirpath_set_outcome(
 56         struct xchk_dirtree             *dl,
 57         struct xchk_dirpath             *path,
 58         enum xchk_dirpath_outcome       outcome)
 59 {
 60         trace_xrep_dirpath_set_outcome(dl->sc, path->path_nr, path->nr_steps,
 61                         outcome);
 62 
 63         path->outcome = outcome;
 64 }
 65 
 66 /* Delete all paths. */
 67 STATIC void
 68 xrep_dirtree_delete_all_paths(
 69         struct xchk_dirtree             *dl,
 70         struct xchk_dirtree_outcomes    *oc)
 71 {
 72         struct xchk_dirpath             *path;
 73 
 74         xchk_dirtree_for_each_path(dl, path) {
 75                 switch (path->outcome) {
 76                 case XCHK_DIRPATH_CORRUPT:
 77                 case XCHK_DIRPATH_LOOP:
 78                         oc->suspect--;
 79                         oc->bad++;
 80                         xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
 81                         break;
 82                 case XCHK_DIRPATH_OK:
 83                         oc->good--;
 84                         oc->bad++;
 85                         xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
 86                         break;
 87                 default:
 88                         break;
 89                 }
 90         }
 91 
 92         ASSERT(oc->suspect == 0);
 93         ASSERT(oc->good == 0);
 94 }
 95 
 96 /* Since this is the surviving path, set the dotdot entry to this value. */
 97 STATIC void
 98 xrep_dirpath_retain_parent(
 99         struct xchk_dirtree             *dl,
100         struct xchk_dirpath             *path)
101 {
102         struct xchk_dirpath_step        step;
103         int                             error;
104 
105         error = xfarray_load(dl->path_steps, path->first_step, &step);
106         if (error)
107                 return;
108 
109         dl->parent_ino = be64_to_cpu(step.pptr_rec.p_ino);
110 }
111 
112 /* Find the one surviving path so we know how to set dotdot. */
113 STATIC void
114 xrep_dirtree_find_surviving_path(
115         struct xchk_dirtree             *dl,
116         struct xchk_dirtree_outcomes    *oc)
117 {
118         struct xchk_dirpath             *path;
119         bool                            foundit = false;
120 
121         xchk_dirtree_for_each_path(dl, path) {
122                 switch (path->outcome) {
123                 case XCHK_DIRPATH_CORRUPT:
124                 case XCHK_DIRPATH_LOOP:
125                 case XCHK_DIRPATH_OK:
126                         if (!foundit) {
127                                 xrep_dirpath_retain_parent(dl, path);
128                                 foundit = true;
129                                 continue;
130                         }
131                         ASSERT(foundit == false);
132                         break;
133                 default:
134                         break;
135                 }
136         }
137 
138         ASSERT(oc->suspect + oc->good == 1);
139 }
140 
141 /* Delete all paths except for the one good one. */
142 STATIC void
143 xrep_dirtree_keep_one_good_path(
144         struct xchk_dirtree             *dl,
145         struct xchk_dirtree_outcomes    *oc)
146 {
147         struct xchk_dirpath             *path;
148         bool                            foundit = false;
149 
150         xchk_dirtree_for_each_path(dl, path) {
151                 switch (path->outcome) {
152                 case XCHK_DIRPATH_CORRUPT:
153                 case XCHK_DIRPATH_LOOP:
154                         oc->suspect--;
155                         oc->bad++;
156                         xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
157                         break;
158                 case XCHK_DIRPATH_OK:
159                         if (!foundit) {
160                                 xrep_dirpath_retain_parent(dl, path);
161                                 foundit = true;
162                                 continue;
163                         }
164                         oc->good--;
165                         oc->bad++;
166                         xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
167                         break;
168                 default:
169                         break;
170                 }
171         }
172 
173         ASSERT(oc->suspect == 0);
174         ASSERT(oc->good < 2);
175 }
176 
177 /* Delete all paths except for one suspect one. */
178 STATIC void
179 xrep_dirtree_keep_one_suspect_path(
180         struct xchk_dirtree             *dl,
181         struct xchk_dirtree_outcomes    *oc)
182 {
183         struct xchk_dirpath             *path;
184         bool                            foundit = false;
185 
186         xchk_dirtree_for_each_path(dl, path) {
187                 switch (path->outcome) {
188                 case XCHK_DIRPATH_CORRUPT:
189                 case XCHK_DIRPATH_LOOP:
190                         if (!foundit) {
191                                 xrep_dirpath_retain_parent(dl, path);
192                                 foundit = true;
193                                 continue;
194                         }
195                         oc->suspect--;
196                         oc->bad++;
197                         xrep_dirpath_set_outcome(dl, path, XCHK_DIRPATH_DELETE);
198                         break;
199                 case XCHK_DIRPATH_OK:
200                         ASSERT(0);
201                         break;
202                 default:
203                         break;
204                 }
205         }
206 
207         ASSERT(oc->suspect == 1);
208         ASSERT(oc->good == 0);
209 }
210 
211 /*
212  * Figure out what to do with the paths we tried to find.  Returns -EDEADLOCK
213  * if the scan results have become stale.
214  */
215 STATIC void
216 xrep_dirtree_decide_fate(
217         struct xchk_dirtree             *dl,
218         struct xchk_dirtree_outcomes    *oc)
219 {
220         xchk_dirtree_evaluate(dl, oc);
221 
222         /* Parentless directories should not have any paths at all. */
223         if (xchk_dirtree_parentless(dl)) {
224                 xrep_dirtree_delete_all_paths(dl, oc);
225                 return;
226         }
227 
228         /* One path is exactly the number of paths we want. */
229         if (oc->good + oc->suspect == 1) {
230                 xrep_dirtree_find_surviving_path(dl, oc);
231                 return;
232         }
233 
234         /* Zero paths means we should reattach the subdir to the orphanage. */
235         if (oc->good + oc->suspect == 0) {
236                 if (dl->sc->orphanage)
237                         oc->needs_adoption = true;
238                 return;
239         }
240 
241         /*
242          * Otherwise, this subdirectory has too many parents.  If there's at
243          * least one good path, keep it and delete the others.
244          */
245         if (oc->good > 0) {
246                 xrep_dirtree_keep_one_good_path(dl, oc);
247                 return;
248         }
249 
250         /*
251          * There are no good paths and there are too many suspect paths.
252          * Keep the first suspect path and delete the rest.
253          */
254         xrep_dirtree_keep_one_suspect_path(dl, oc);
255 }
256 
257 /*
258  * Load the first step of this path into @step and @dl->xname/pptr
259  * for later repair work.
260  */
261 STATIC int
262 xrep_dirtree_prep_path(
263         struct xchk_dirtree             *dl,
264         struct xchk_dirpath             *path,
265         struct xchk_dirpath_step        *step)
266 {
267         int                             error;
268 
269         error = xfarray_load(dl->path_steps, path->first_step, step);
270         if (error)
271                 return error;
272 
273         error = xfblob_loadname(dl->path_names, step->name_cookie, &dl->xname,
274                         step->name_len);
275         if (error)
276                 return error;
277 
278         dl->pptr_rec = step->pptr_rec; /* struct copy */
279         return 0;
280 }
281 
282 /* Delete the VFS dentry for a removed child. */
283 STATIC int
284 xrep_dirtree_purge_dentry(
285         struct xchk_dirtree     *dl,
286         struct xfs_inode        *dp,
287         const struct xfs_name   *name)
288 {
289         struct qstr             qname = QSTR_INIT(name->name, name->len);
290         struct dentry           *parent_dentry, *child_dentry;
291         int                     error = 0;
292 
293         /*
294          * Find the dentry for the parent directory.  If there isn't one, we're
295          * done.  Caller already holds i_rwsem for parent and child.
296          */
297         parent_dentry = d_find_alias(VFS_I(dp));
298         if (!parent_dentry)
299                 return 0;
300 
301         /* The VFS thinks the parent is a directory, right? */
302         if (!d_is_dir(parent_dentry)) {
303                 ASSERT(d_is_dir(parent_dentry));
304                 error = -EFSCORRUPTED;
305                 goto out_dput_parent;
306         }
307 
308         /*
309          * Try to find the dirent pointing to the child.  If there isn't one,
310          * we're done.
311          */
312         qname.hash = full_name_hash(parent_dentry, name->name, name->len);
313         child_dentry = d_lookup(parent_dentry, &qname);
314         if (!child_dentry) {
315                 error = 0;
316                 goto out_dput_parent;
317         }
318 
319         trace_xrep_dirtree_delete_child(dp->i_mount, child_dentry);
320 
321         /* Child is not a directory?  We're screwed. */
322         if (!d_is_dir(child_dentry)) {
323                 ASSERT(d_is_dir(child_dentry));
324                 error = -EFSCORRUPTED;
325                 goto out_dput_child;
326         }
327 
328         /* Replace the child dentry with a negative one. */
329         d_delete(child_dentry);
330 
331 out_dput_child:
332         dput(child_dentry);
333 out_dput_parent:
334         dput(parent_dentry);
335         return error;
336 }
337 
338 /*
339  * Prepare to delete a link by taking the IOLOCK of the parent and the child
340  * (scrub target).  Caller must hold IOLOCK_EXCL on @sc->ip.  Returns 0 if we
341  * took both locks, or a negative errno if we couldn't lock the parent in time.
342  */
343 static inline int
344 xrep_dirtree_unlink_iolock(
345         struct xfs_scrub        *sc,
346         struct xfs_inode        *dp)
347 {
348         int                     error;
349 
350         ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);
351 
352         if (xfs_ilock_nowait(dp, XFS_IOLOCK_EXCL))
353                 return 0;
354 
355         xchk_iunlock(sc, XFS_IOLOCK_EXCL);
356         do {
357                 xfs_ilock(dp, XFS_IOLOCK_EXCL);
358                 if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))
359                         break;
360                 xfs_iunlock(dp, XFS_IOLOCK_EXCL);
361 
362                 if (xchk_should_terminate(sc, &error)) {
363                         xchk_ilock(sc, XFS_IOLOCK_EXCL);
364                         return error;
365                 }
366 
367                 delay(1);
368         } while (1);
369 
370         return 0;
371 }
372 
373 /*
374  * Remove a link from the directory tree and update the dcache.  Returns
375  * -ESTALE if the scan data are now out of date.
376  */
377 STATIC int
378 xrep_dirtree_unlink(
379         struct xchk_dirtree             *dl,
380         struct xfs_inode                *dp,
381         struct xchk_dirpath             *path,
382         struct xchk_dirpath_step        *step)
383 {
384         struct xfs_scrub                *sc = dl->sc;
385         struct xfs_mount                *mp = sc->mp;
386         xfs_ino_t                       dotdot_ino;
387         xfs_ino_t                       parent_ino = dl->parent_ino;
388         unsigned int                    resblks;
389         int                             dontcare;
390         int                             error;
391 
392         /* Take IOLOCK_EXCL of the parent and child. */
393         error = xrep_dirtree_unlink_iolock(sc, dp);
394         if (error)
395                 return error;
396 
397         /*
398          * Create the transaction that we need to sever the path.  Ignore
399          * EDQUOT and ENOSPC being returned via nospace_error because the
400          * directory code can handle a reservationless update.
401          */
402         resblks = xfs_remove_space_res(mp, step->name_len);
403         error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip,
404                         &resblks, &sc->tp, &dontcare);
405         if (error)
406                 goto out_iolock;
407 
408         /*
409          * Cancel if someone invalidate the paths while we were trying to get
410          * the ILOCK.
411          */
412         mutex_lock(&dl->lock);
413         if (dl->stale) {
414                 mutex_unlock(&dl->lock);
415                 error = -ESTALE;
416                 goto out_trans_cancel;
417         }
418         xrep_dirpath_set_outcome(dl, path, XREP_DIRPATH_DELETING);
419         mutex_unlock(&dl->lock);
420 
421         trace_xrep_dirtree_delete_path(dl->sc, sc->ip, path->path_nr,
422                         &dl->xname, &dl->pptr_rec);
423 
424         /*
425          * Decide if we need to reset the dotdot entry.  Rules:
426          *
427          * - If there's a surviving parent, we want dotdot to point there.
428          * - If we don't have any surviving parents, then point dotdot at the
429          *   root dir.
430          * - If dotdot is already set to the value we want, pass in NULLFSINO
431          *   for no change necessary.
432          *
433          * Do this /before/ we dirty anything, in case the dotdot lookup
434          * fails.
435          */
436         error = xchk_dir_lookup(sc, sc->ip, &xfs_name_dotdot, &dotdot_ino);
437         if (error)
438                 goto out_trans_cancel;
439         if (parent_ino == NULLFSINO)
440                 parent_ino = dl->root_ino;
441         if (dotdot_ino == parent_ino)
442                 parent_ino = NULLFSINO;
443 
444         /* Drop the link from sc->ip's dotdot entry.  */
445         error = xfs_droplink(sc->tp, dp);
446         if (error)
447                 goto out_trans_cancel;
448 
449         /* Reset the dotdot entry to a surviving parent. */
450         if (parent_ino != NULLFSINO) {
451                 error = xfs_dir_replace(sc->tp, sc->ip, &xfs_name_dotdot,
452                                 parent_ino, 0);
453                 if (error)
454                         goto out_trans_cancel;
455         }
456 
457         /* Drop the link from dp to sc->ip. */
458         error = xfs_droplink(sc->tp, sc->ip);
459         if (error)
460                 goto out_trans_cancel;
461 
462         error = xfs_dir_removename(sc->tp, dp, &dl->xname, sc->ip->i_ino,
463                         resblks);
464         if (error) {
465                 ASSERT(error != -ENOENT);
466                 goto out_trans_cancel;
467         }
468 
469         if (xfs_has_parent(sc->mp)) {
470                 error = xfs_parent_removename(sc->tp, &dl->ppargs, dp,
471                                 &dl->xname, sc->ip);
472                 if (error)
473                         goto out_trans_cancel;
474         }
475 
476         /*
477          * Notify dirent hooks that we removed the bad link, invalidate the
478          * dcache, and commit the repair.
479          */
480         xfs_dir_update_hook(dp, sc->ip, -1, &dl->xname);
481         error = xrep_dirtree_purge_dentry(dl, dp, &dl->xname);
482         if (error)
483                 goto out_trans_cancel;
484 
485         error = xrep_trans_commit(sc);
486         goto out_ilock;
487 
488 out_trans_cancel:
489         xchk_trans_cancel(sc);
490 out_ilock:
491         xfs_iunlock(sc->ip, XFS_ILOCK_EXCL);
492         xfs_iunlock(dp, XFS_ILOCK_EXCL);
493 out_iolock:
494         xfs_iunlock(dp, XFS_IOLOCK_EXCL);
495         return error;
496 }
497 
498 /*
499  * Delete a directory entry that points to this directory.  Returns -ESTALE
500  * if the scan data are now out of date.
501  */
502 STATIC int
503 xrep_dirtree_delete_path(
504         struct xchk_dirtree             *dl,
505         struct xchk_dirpath             *path)
506 {
507         struct xchk_dirpath_step        step;
508         struct xfs_scrub                *sc = dl->sc;
509         struct xfs_inode                *dp;
510         int                             error;
511 
512         /*
513          * Load the parent pointer and directory inode for this path, then
514          * drop the scan lock, the ILOCK, and the transaction so that
515          * _delete_path can reserve the proper transaction.  This sets up
516          * @dl->xname for the deletion.
517          */
518         error = xrep_dirtree_prep_path(dl, path, &step);
519         if (error)
520                 return error;
521 
522         error = xchk_iget(sc, be64_to_cpu(step.pptr_rec.p_ino), &dp);
523         if (error)
524                 return error;
525 
526         mutex_unlock(&dl->lock);
527         xchk_trans_cancel(sc);
528         xchk_iunlock(sc, XFS_ILOCK_EXCL);
529 
530         /* Delete the directory link and release the parent. */
531         error = xrep_dirtree_unlink(dl, dp, path, &step);
532         xchk_irele(sc, dp);
533 
534         /*
535          * Retake all the resources we had at the beginning even if the repair
536          * failed or the scan data are now stale.  This keeps things simple for
537          * the caller.
538          */
539         xchk_trans_alloc_empty(sc);
540         xchk_ilock(sc, XFS_ILOCK_EXCL);
541         mutex_lock(&dl->lock);
542 
543         if (!error && dl->stale)
544                 error = -ESTALE;
545         return error;
546 }
547 
548 /* Add a new path to represent our in-progress adoption. */
549 STATIC int
550 xrep_dirtree_create_adoption_path(
551         struct xchk_dirtree             *dl)
552 {
553         struct xfs_scrub                *sc = dl->sc;
554         struct xchk_dirpath             *path;
555         int                             error;
556 
557         /*
558          * We should have capped the number of paths at XFS_MAXLINK-1 in the
559          * scanner.
560          */
561         if (dl->nr_paths > XFS_MAXLINK) {
562                 ASSERT(dl->nr_paths <= XFS_MAXLINK);
563                 return -EFSCORRUPTED;
564         }
565 
566         /*
567          * Create a new xchk_path structure to remember this parent pointer
568          * and record the first name step.
569          */
570         path = kmalloc(sizeof(struct xchk_dirpath), XCHK_GFP_FLAGS);
571         if (!path)
572                 return -ENOMEM;
573 
574         INIT_LIST_HEAD(&path->list);
575         xino_bitmap_init(&path->seen_inodes);
576         path->nr_steps = 0;
577         path->outcome = XREP_DIRPATH_ADOPTING;
578 
579         /*
580          * Record the new link that we just created in the orphanage.  Because
581          * adoption is the last repair that we perform, we don't bother filling
582          * in the path all the way back to the root.
583          */
584         xfs_inode_to_parent_rec(&dl->pptr_rec, sc->orphanage);
585 
586         error = xino_bitmap_set(&path->seen_inodes, sc->orphanage->i_ino);
587         if (error)
588                 goto out_path;
589 
590         trace_xrep_dirtree_create_adoption(sc, sc->ip, dl->nr_paths,
591                         &dl->xname, &dl->pptr_rec);
592 
593         error = xchk_dirpath_append(dl, sc->ip, path, &dl->xname,
594                         &dl->pptr_rec);
595         if (error)
596                 goto out_path;
597 
598         path->first_step = xfarray_length(dl->path_steps) - 1;
599         path->second_step = XFARRAY_NULLIDX;
600         path->path_nr = dl->nr_paths;
601 
602         list_add_tail(&path->list, &dl->path_list);
603         dl->nr_paths++;
604         return 0;
605 
606 out_path:
607         kfree(path);
608         return error;
609 }
610 
611 /*
612  * Prepare to move a file to the orphanage by taking the IOLOCK of the
613  * orphanage and the child (scrub target).  Caller must hold IOLOCK_EXCL on
614  * @sc->ip.  Returns 0 if we took both locks, or a negative errno if we
615  * couldn't lock the orphanage in time.
616  */
617 static inline int
618 xrep_dirtree_adopt_iolock(
619         struct xfs_scrub        *sc)
620 {
621         int                     error;
622 
623         ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL);
624 
625         if (xrep_orphanage_ilock_nowait(sc, XFS_IOLOCK_EXCL))
626                 return 0;
627 
628         xchk_iunlock(sc, XFS_IOLOCK_EXCL);
629         do {
630                 xrep_orphanage_ilock(sc, XFS_IOLOCK_EXCL);
631                 if (xchk_ilock_nowait(sc, XFS_IOLOCK_EXCL))
632                         break;
633                 xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
634 
635                 if (xchk_should_terminate(sc, &error)) {
636                         xchk_ilock(sc, XFS_IOLOCK_EXCL);
637                         return error;
638                 }
639 
640                 delay(1);
641         } while (1);
642 
643         return 0;
644 }
645 
646 /*
647  * Reattach this orphaned directory to the orphanage.  Do not call this with
648  * any resources held.  Returns -ESTALE if the scan data have become out of
649  * date.
650  */
651 STATIC int
652 xrep_dirtree_adopt(
653         struct xchk_dirtree             *dl)
654 {
655         struct xfs_scrub                *sc = dl->sc;
656         int                             error;
657 
658         /* Take the IOLOCK of the orphanage and the scrub target. */
659         error = xrep_dirtree_adopt_iolock(sc);
660         if (error)
661                 return error;
662 
663         /*
664          * Set up for an adoption.  The directory tree fixer runs after the
665          * link counts have been corrected.  Therefore, we must bump the
666          * child's link count since there will be no further opportunity to fix
667          * errors.
668          */
669         error = xrep_adoption_trans_alloc(sc, &dl->adoption);
670         if (error)
671                 goto out_iolock;
672         dl->adoption.bump_child_nlink = true;
673 
674         /* Figure out what name we're going to use here. */
675         error = xrep_adoption_compute_name(&dl->adoption, &dl->xname);
676         if (error)
677                 goto out_trans;
678 
679         /*
680          * Now that we have a proposed name for the orphanage entry, create
681          * a faux path so that the live update hook will see it.
682          */
683         mutex_lock(&dl->lock);
684         if (dl->stale) {
685                 mutex_unlock(&dl->lock);
686                 error = -ESTALE;
687                 goto out_trans;
688         }
689         error = xrep_dirtree_create_adoption_path(dl);
690         mutex_unlock(&dl->lock);
691         if (error)
692                 goto out_trans;
693 
694         /* Reparent the directory. */
695         error = xrep_adoption_move(&dl->adoption);
696         if (error)
697                 goto out_trans;
698 
699         /*
700          * Commit the name and release all inode locks except for the scrub
701          * target's IOLOCK.
702          */
703         error = xrep_trans_commit(sc);
704         goto out_ilock;
705 
706 out_trans:
707         xchk_trans_cancel(sc);
708 out_ilock:
709         xchk_iunlock(sc, XFS_ILOCK_EXCL);
710         xrep_orphanage_iunlock(sc, XFS_ILOCK_EXCL);
711 out_iolock:
712         xrep_orphanage_iunlock(sc, XFS_IOLOCK_EXCL);
713         return error;
714 }
715 
716 /*
717  * This newly orphaned directory needs to be adopted by the orphanage.
718  * Make this happen.
719  */
720 STATIC int
721 xrep_dirtree_move_to_orphanage(
722         struct xchk_dirtree             *dl)
723 {
724         struct xfs_scrub                *sc = dl->sc;
725         int                             error;
726 
727         /*
728          * Start by dropping all the resources that we hold so that we can grab
729          * all the resources that we need for the adoption.
730          */
731         mutex_unlock(&dl->lock);
732         xchk_trans_cancel(sc);
733         xchk_iunlock(sc, XFS_ILOCK_EXCL);
734 
735         /* Perform the adoption. */
736         error = xrep_dirtree_adopt(dl);
737 
738         /*
739          * Retake all the resources we had at the beginning even if the repair
740          * failed or the scan data are now stale.  This keeps things simple for
741          * the caller.
742          */
743         xchk_trans_alloc_empty(sc);
744         xchk_ilock(sc, XFS_ILOCK_EXCL);
745         mutex_lock(&dl->lock);
746 
747         if (!error && dl->stale)
748                 error = -ESTALE;
749         return error;
750 }
751 
752 /*
753  * Try to fix all the problems.  Returns -ESTALE if the scan data have become
754  * out of date.
755  */
756 STATIC int
757 xrep_dirtree_fix_problems(
758         struct xchk_dirtree             *dl,
759         struct xchk_dirtree_outcomes    *oc)
760 {
761         struct xchk_dirpath             *path;
762         int                             error;
763 
764         /* Delete all the paths we don't want. */
765         xchk_dirtree_for_each_path(dl, path) {
766                 if (path->outcome != XCHK_DIRPATH_DELETE)
767                         continue;
768 
769                 error = xrep_dirtree_delete_path(dl, path);
770                 if (error)
771                         return error;
772         }
773 
774         /* Reparent this directory to the orphanage. */
775         if (oc->needs_adoption) {
776                 if (xrep_orphanage_can_adopt(dl->sc))
777                         return xrep_dirtree_move_to_orphanage(dl);
778                 return -EFSCORRUPTED;
779         }
780 
781         return 0;
782 }
783 
784 /* Fix directory loops involving this directory. */
785 int
786 xrep_dirtree(
787         struct xfs_scrub                *sc)
788 {
789         struct xchk_dirtree             *dl = sc->buf;
790         struct xchk_dirtree_outcomes    oc;
791         int                             error;
792 
793         /*
794          * Prepare to fix the directory tree by retaking the scan lock.  The
795          * order of resource acquisition is still IOLOCK -> transaction ->
796          * ILOCK -> scan lock.
797          */
798         mutex_lock(&dl->lock);
799         do {
800                 /*
801                  * Decide what we're going to do, then do it.  An -ESTALE
802                  * return here means the scan results are invalid and we have
803                  * to walk again.
804                  */
805                 if (!dl->stale) {
806                         xrep_dirtree_decide_fate(dl, &oc);
807 
808                         trace_xrep_dirtree_decided_fate(dl, &oc);
809 
810                         error = xrep_dirtree_fix_problems(dl, &oc);
811                         if (!error || error != -ESTALE)
812                                 break;
813                 }
814                 error = xchk_dirtree_find_paths_to_root(dl);
815                 if (error == -ELNRNG || error == -ENOSR)
816                         error = -EFSCORRUPTED;
817         } while (!error);
818         mutex_unlock(&dl->lock);
819 
820         return error;
821 }
822 

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php