]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - scrub/vfs.c
xfs_scrub: update copyright years for scrub/ files
[thirdparty/xfsprogs-dev.git] / scrub / vfs.c
index a33ec8b4a29b363377ee3a9ee5f89369b70d1128..9e459d6243fa4428dd27de192fa5935db9ee8fcf 100644 (file)
@@ -1,7 +1,7 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0-or-later
 /*
- * Copyright (C) 2018 Oracle.  All Rights Reserved.
- * Author: Darrick J. Wong <darrick.wong@oracle.com>
+ * Copyright (C) 2018-2024 Oracle.  All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
  */
 #include "xfs.h"
 #include <stdint.h>
@@ -9,8 +9,8 @@
 #include <sys/types.h>
 #include <sys/statvfs.h>
 #include "handle.h"
-#include "path.h"
-#include "workqueue.h"
+#include "libfrog/paths.h"
+#include "libfrog/workqueue.h"
 #include "xfs_scrub.h"
 #include "common.h"
 #include "vfs.h"
@@ -30,7 +30,7 @@ struct scan_fs_tree {
        pthread_mutex_t         lock;
        pthread_cond_t          wakeup;
        struct stat             root_sb;
-       bool                    moveon;
+       bool                    aborted;
        scan_fs_tree_dir_fn     dir_fn;
        scan_fs_tree_dirent_fn  dirent_fn;
        void                    *arg;
@@ -43,6 +43,75 @@ struct scan_fs_tree_dir {
        bool                    rootdir;
 };
 
+static void scan_fs_dir(struct workqueue *wq, xfs_agnumber_t agno, void *arg);
+
+/* Increment the number of directories that are queued for processing. */
+static void
+inc_nr_dirs(
+       struct scan_fs_tree     *sft)
+{
+       pthread_mutex_lock(&sft->lock);
+       sft->nr_dirs++;
+       pthread_mutex_unlock(&sft->lock);
+}
+
+/*
+ * Decrement the number of directories that are queued for processing and if
+ * we ran out of dirs to process, wake up anyone who was waiting for processing
+ * to finish.
+ */
+static void
+dec_nr_dirs(
+       struct scan_fs_tree     *sft)
+{
+       pthread_mutex_lock(&sft->lock);
+       sft->nr_dirs--;
+       if (sft->nr_dirs == 0)
+               pthread_cond_signal(&sft->wakeup);
+       pthread_mutex_unlock(&sft->lock);
+}
+
+/* Queue a directory for scanning. */
+static int
+queue_subdir(
+       struct scrub_ctx        *ctx,
+       struct scan_fs_tree     *sft,
+       struct workqueue        *wq,
+       const char              *path,
+       bool                    is_rootdir)
+{
+       struct scan_fs_tree_dir *new_sftd;
+       int                     error;
+
+       new_sftd = malloc(sizeof(struct scan_fs_tree_dir));
+       if (!new_sftd)
+               return errno;
+
+       new_sftd->path = strdup(path);
+       if (!new_sftd->path) {
+               error = errno;
+               goto out_sftd;
+       }
+
+       new_sftd->sft = sft;
+       new_sftd->rootdir = is_rootdir;
+
+       inc_nr_dirs(sft);
+       error = -workqueue_add(wq, scan_fs_dir, 0, new_sftd);
+       if (error) {
+               dec_nr_dirs(sft);
+               str_liberror(ctx, error, _("queueing directory scan work"));
+               goto out_path;
+       }
+
+       return 0;
+out_path:
+       free(new_sftd->path);
+out_sftd:
+       free(new_sftd);
+       return error;
+}
+
 /* Scan a directory sub tree. */
 static void
 scan_fs_dir(
@@ -56,7 +125,6 @@ scan_fs_dir(
        DIR                     *dir;
        struct dirent           *dirent;
        char                    newpath[PATH_MAX];
-       struct scan_fs_tree_dir *new_sftd;
        struct stat             sb;
        int                     dir_fd;
        int                     error;
@@ -70,8 +138,9 @@ scan_fs_dir(
        }
 
        /* Caller-specific directory checks. */
-       if (!sft->dir_fn(ctx, sftd->path, dir_fd, sft->arg)) {
-               sft->moveon = false;
+       error = sft->dir_fn(ctx, sftd->path, dir_fd, sft->arg);
+       if (error) {
+               sft->aborted = true;
                error = close(dir_fd);
                if (error)
                        str_errno(ctx, sftd->path);
@@ -82,11 +151,14 @@ scan_fs_dir(
        dir = fdopendir(dir_fd);
        if (!dir) {
                str_errno(ctx, sftd->path);
+               sft->aborted = true;
                close(dir_fd);
                goto out;
        }
        rewinddir(dir);
-       for (dirent = readdir(dir); dirent != NULL; dirent = readdir(dir)) {
+       for (dirent = readdir(dir);
+            !sft->aborted && dirent != NULL;
+            dirent = readdir(dir)) {
                snprintf(newpath, PATH_MAX, "%s/%s", sftd->path,
                                dirent->d_name);
 
@@ -103,37 +175,26 @@ scan_fs_dir(
                        continue;
 
                /* Caller-specific directory entry function. */
-               if (!sft->dirent_fn(ctx, newpath, dir_fd, dirent, &sb,
-                               sft->arg)) {
-                       sft->moveon = false;
+               error = sft->dirent_fn(ctx, newpath, dir_fd, dirent, &sb,
+                               sft->arg);
+               if (error) {
+                       sft->aborted = true;
                        break;
                }
 
-               if (xfs_scrub_excessive_errors(ctx)) {
-                       sft->moveon = false;
+               if (scrub_excessive_errors(ctx)) {
+                       sft->aborted = true;
                        break;
                }
 
                /* If directory, call ourselves recursively. */
                if (S_ISDIR(sb.st_mode) && strcmp(".", dirent->d_name) &&
                    strcmp("..", dirent->d_name)) {
-                       new_sftd = malloc(sizeof(struct scan_fs_tree_dir));
-                       if (!new_sftd) {
-                               str_errno(ctx, newpath);
-                               sft->moveon = false;
-                               break;
-                       }
-                       new_sftd->path = strdup(newpath);
-                       new_sftd->sft = sft;
-                       new_sftd->rootdir = false;
-                       pthread_mutex_lock(&sft->lock);
-                       sft->nr_dirs++;
-                       pthread_mutex_unlock(&sft->lock);
-                       error = workqueue_add(wq, scan_fs_dir, 0, new_sftd);
+                       error = queue_subdir(ctx, sft, wq, newpath, false);
                        if (error) {
-                               str_info(ctx, ctx->mntpoint,
-_("Could not queue subdirectory scan work."));
-                               sft->moveon = false;
+                               str_liberror(ctx, error,
+_("queueing subdirectory scan"));
+                               sft->aborted = true;
                                break;
                        }
                }
@@ -145,18 +206,16 @@ _("Could not queue subdirectory scan work."));
                str_errno(ctx, sftd->path);
 
 out:
-       pthread_mutex_lock(&sft->lock);
-       sft->nr_dirs--;
-       if (sft->nr_dirs == 0)
-               pthread_cond_signal(&sft->wakeup);
-       pthread_mutex_unlock(&sft->lock);
-
+       dec_nr_dirs(sft);
        free(sftd->path);
        free(sftd);
 }
 
-/* Scan the entire filesystem. */
-bool
+/*
+ * Scan the entire filesystem.  This function returns 0 on success; if there
+ * are errors, this function will log them and returns nonzero.
+ */
+int
 scan_fs_tree(
        struct scrub_ctx        *ctx,
        scan_fs_tree_dir_fn     dir_fn,
@@ -164,52 +223,67 @@ scan_fs_tree(
        void                    *arg)
 {
        struct workqueue        wq;
-       struct scan_fs_tree     sft;
-       struct scan_fs_tree_dir *sftd;
+       struct scan_fs_tree     sft = {
+               .root_sb        = ctx->mnt_sb,
+               .dir_fn         = dir_fn,
+               .dirent_fn      = dirent_fn,
+               .arg            = arg,
+       };
        int                     ret;
 
-       sft.moveon = true;
-       sft.nr_dirs = 1;
-       sft.root_sb = ctx->mnt_sb;
-       sft.dir_fn = dir_fn;
-       sft.dirent_fn = dirent_fn;
-       sft.arg = arg;
-       pthread_mutex_init(&sft.lock, NULL);
-       pthread_cond_init(&sft.wakeup, NULL);
-
-       sftd = malloc(sizeof(struct scan_fs_tree_dir));
-       if (!sftd) {
-               str_errno(ctx, ctx->mntpoint);
-               return false;
+       ret = pthread_mutex_init(&sft.lock, NULL);
+       if (ret) {
+               str_liberror(ctx, ret, _("creating directory scan lock"));
+               return ret;
+       }
+       ret = pthread_cond_init(&sft.wakeup, NULL);
+       if (ret) {
+               str_liberror(ctx, ret, _("creating directory scan signal"));
+               goto out_mutex;
        }
-       sftd->path = strdup(ctx->mntpoint);
-       sftd->sft = &sft;
-       sftd->rootdir = true;
 
-       ret = workqueue_create(&wq, (struct xfs_mount *)ctx,
+       ret = -workqueue_create(&wq, (struct xfs_mount *)ctx,
                        scrub_nproc_workqueue(ctx));
        if (ret) {
-               str_info(ctx, ctx->mntpoint, _("Could not create workqueue."));
-               goto out_free;
+               str_liberror(ctx, ret, _("creating directory scan workqueue"));
+               goto out_cond;
        }
-       ret = workqueue_add(&wq, scan_fs_dir, 0, sftd);
+
+       ret = queue_subdir(ctx, &sft, &wq, ctx->mntpoint, true);
        if (ret) {
-               str_info(ctx, ctx->mntpoint,
-_("Could not queue directory scan work."));
-               goto out_free;
+               str_liberror(ctx, ret, _("queueing directory scan"));
+               goto out_wq;
        }
 
+       /*
+        * Wait for the wakeup to trigger, which should only happen when the
+        * last worker thread decrements nr_dirs to zero.  Once the worker
+        * triggers the wakeup and unlocks the sft lock, it's no longer safe
+        * for any worker thread to access sft, as we now own the lock and are
+        * about to tear everything down.
+        */
        pthread_mutex_lock(&sft.lock);
-       pthread_cond_wait(&sft.wakeup, &sft.lock);
+       while (sft.nr_dirs > 0)
+               pthread_cond_wait(&sft.wakeup, &sft.lock);
        assert(sft.nr_dirs == 0);
        pthread_mutex_unlock(&sft.lock);
-       workqueue_destroy(&wq);
 
-       return sft.moveon;
-out_free:
-       free(sftd->path);
-       free(sftd);
-       return false;
+       ret = -workqueue_terminate(&wq);
+       if (ret) {
+               str_liberror(ctx, ret, _("finishing directory scan work"));
+               goto out_wq;
+       }
+
+       if (!ret && sft.aborted)
+               ret = -1;
+
+out_wq:
+       workqueue_destroy(&wq);
+out_cond:
+       pthread_cond_destroy(&sft.wakeup);
+out_mutex:
+       pthread_mutex_destroy(&sft.lock);
+       return ret;
 }
 
 #ifndef FITRIM
@@ -230,7 +304,7 @@ fstrim(
        int                     error;
 
        range.len = ULLONG_MAX;
-       error = ioctl(ctx->mnt_fd, FITRIM, &range);
+       error = ioctl(ctx->mnt.fd, FITRIM, &range);
        if (error && errno != EOPNOTSUPP && errno != ENOTTY)
                perror(_("fstrim"));
 }