]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - scrub/common.c
misc: add static to various sourcefile-local functions
[thirdparty/xfsprogs-dev.git] / scrub / common.c
index 8137881fbf46dd048a6b8063c06aa5b1e28cfb38..49a87f412c4dcf65753d37ca73e6f679da5dc4f1 100644 (file)
@@ -1,29 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2018 Oracle.  All Rights Reserved.
- *
  * Author: Darrick J. Wong <darrick.wong@oracle.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it would be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write the Free Software Foundation,
- * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-#include <stdio.h>
+#include "xfs.h"
 #include <pthread.h>
-#include <stdbool.h>
+#include <sys/statvfs.h>
+#include <syslog.h>
 #include "platform_defs.h"
-#include "xfs.h"
+#include "libfrog/paths.h"
 #include "xfs_scrub.h"
 #include "common.h"
+#include "progress.h"
+
+extern char            *progname;
 
 /*
  * Reporting Status to the Console
 
 /* Too many errors? Bail out. */
 bool
-xfs_scrub_excessive_errors(
+scrub_excessive_errors(
        struct scrub_ctx        *ctx)
 {
-       bool                    ret;
+       unsigned long long      errors_seen;
+
+       /*
+        * We only set max_errors at the start of the program, so it's safe to
+        * access it locklessly.
+        */
+       if (ctx->max_errors == 0)
+               return false;
 
        pthread_mutex_lock(&ctx->lock);
-       ret = ctx->max_errors > 0 && ctx->errors_found >= ctx->max_errors;
+       errors_seen = ctx->corruptions_found + ctx->unfixable_errors;
        pthread_mutex_unlock(&ctx->lock);
 
-       return ret;
+       return errors_seen >= ctx->max_errors;
 }
 
-static const char *err_str[] = {
-       [S_ERROR]       = "Error",
-       [S_WARN]        = "Warning",
-       [S_INFO]        = "Info",
+static struct {
+       const char *string;
+       int loglevel;
+} err_levels[] = {
+       [S_ERROR]  = {
+               .string = "Error",
+               .loglevel = LOG_ERR,
+       },
+       [S_CORRUPT] = {
+               .string = "Corruption",
+               .loglevel = LOG_ERR,
+       },
+       [S_UNFIXABLE] = {
+               .string = "Unfixable Error",
+               .loglevel = LOG_ERR,
+       },
+       [S_WARN]   = {
+               .string = "Warning",
+               .loglevel = LOG_WARNING,
+       },
+       [S_INFO]   = {
+               .string = "Info",
+               .loglevel = LOG_INFO,
+       },
+       [S_REPAIR] = {
+               .string = "Repaired",
+               .loglevel = LOG_INFO,
+       },
+       [S_PREEN]  = {
+               .string = "Optimized",
+               .loglevel = LOG_INFO,
+       },
 };
 
+/* If stream is a tty, clear to end of line to clean up progress bar. */
+static inline const char *stream_start(FILE *stream)
+{
+       if (stream == stderr)
+               return stderr_isatty ? CLEAR_EOL : "";
+       return stdout_isatty ? CLEAR_EOL : "";
+}
+
 /* Print a warning string and some warning text. */
 void
 __str_out(
@@ -81,7 +114,13 @@ __str_out(
                stream = stdout;
 
        pthread_mutex_lock(&ctx->lock);
-       fprintf(stream, "%s: %s: ", _(err_str[level]), descr);
+
+       /* We only want to hear about optimizing when in debug/verbose mode. */
+       if (level == S_PREEN && !debug && !verbose)
+               goto out_record;
+
+       fprintf(stream, "%s%s: %s: ", stream_start(stream),
+                       _(err_levels[level].string), descr);
        if (error) {
                fprintf(stream, _("%s."), strerror_r(error, buf, DESCR_BUFSZ));
        } else {
@@ -96,12 +135,287 @@ __str_out(
        if (stream == stdout)
                fflush(stream);
 
-       if (error)      /* A syscall failed */
+out_record:
+       if (error || level == S_ERROR)      /* A syscall failed */
                ctx->runtime_errors++;
-       else if (level == S_ERROR)
-               ctx->errors_found++;
+       else if (level == S_CORRUPT)
+               ctx->corruptions_found++;
+       else if (level == S_UNFIXABLE)
+               ctx->unfixable_errors++;
        else if (level == S_WARN)
                ctx->warnings_found++;
+       else if (level == S_REPAIR)
+               ctx->repairs++;
+       else if (level == S_PREEN)
+               ctx->preens++;
+
+       pthread_mutex_unlock(&ctx->lock);
+}
+
+/* Log a message to syslog. */
+#define LOG_BUFSZ      4096
+#define LOGNAME_BUFSZ  256
+void
+__str_log(
+       struct scrub_ctx        *ctx,
+       enum error_level        level,
+       const char              *format,
+       ...)
+{
+       va_list                 args;
+       char                    logname[LOGNAME_BUFSZ];
+       char                    buf[LOG_BUFSZ];
+       int                     sz;
+
+       /* We only want to hear about optimizing when in debug/verbose mode. */
+       if (level == S_PREEN && !debug && !verbose)
+               return;
+
+       /*
+        * Skip logging if we're being run as a service (presumably the
+        * service will log stdout/stderr); if we're being run in a non
+        * interactive manner (assume we're a service); or if we're in
+        * debug mode.
+        */
+       if (is_service || !isatty(fileno(stdin)) || debug)
+               return;
+
+       snprintf(logname, LOGNAME_BUFSZ, "%s@%s", progname, ctx->mntpoint);
+       openlog(logname, LOG_PID, LOG_DAEMON);
+
+       sz = snprintf(buf, LOG_BUFSZ, "%s: ", _(err_levels[level].string));
+       va_start(args, format);
+       vsnprintf(buf + sz, LOG_BUFSZ - sz, format, args);
+       va_end(args);
+       syslog(err_levels[level].loglevel, "%s", buf);
+
+       closelog();
+}
+
+double
+timeval_subtract(
+       struct timeval          *tv1,
+       struct timeval          *tv2)
+{
+       return ((tv1->tv_sec - tv2->tv_sec) +
+               ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
+}
+
+/* Produce human readable disk space output. */
+double
+auto_space_units(
+       unsigned long long      bytes,
+       char                    **units)
+{
+       if (debug > 1)
+               goto no_prefix;
+       if (bytes > (1ULL << 40)) {
+               *units = "TiB";
+               return (double)bytes / (1ULL << 40);
+       } else if (bytes > (1ULL << 30)) {
+               *units = "GiB";
+               return (double)bytes / (1ULL << 30);
+       } else if (bytes > (1ULL << 20)) {
+               *units = "MiB";
+               return (double)bytes / (1ULL << 20);
+       } else if (bytes > (1ULL << 10)) {
+               *units = "KiB";
+               return (double)bytes / (1ULL << 10);
+       }
+
+no_prefix:
+       *units = "B";
+       return bytes;
+}
+
+/* Produce human readable discrete number output. */
+double
+auto_units(
+       unsigned long long      number,
+       char                    **units,
+       int                     *precision)
+{
+       if (debug > 1)
+               goto no_prefix;
+       *precision = 1;
+       if (number > 1000000000000ULL) {
+               *units = "T";
+               return number / 1000000000000.0;
+       } else if (number > 1000000000ULL) {
+               *units = "G";
+               return number / 1000000000.0;
+       } else if (number > 1000000ULL) {
+               *units = "M";
+               return number / 1000000.0;
+       } else if (number > 1000ULL) {
+               *units = "K";
+               return number / 1000.0;
+       }
+
+no_prefix:
+       *units = "";
+       *precision = 0;
+       return number;
+}
+
+/* How many threads to kick off? */
+unsigned int
+scrub_nproc(
+       struct scrub_ctx        *ctx)
+{
+       if (force_nr_threads)
+               return force_nr_threads;
+       return ctx->nr_io_threads;
+}
+
+/*
+ * How many threads to kick off for a workqueue?  If we only want one
+ * thread, save ourselves the overhead and just run it in the main thread.
+ */
+unsigned int
+scrub_nproc_workqueue(
+       struct scrub_ctx        *ctx)
+{
+       unsigned int            x;
 
+       x = scrub_nproc(ctx);
+       if (x == 1)
+               x = 0;
+       return x;
+}
+
+/*
+ * Sleep for 100us * however many -b we got past the initial one.
+ * This is an (albeit clumsy) way to throttle scrub activity.
+ */
+void
+background_sleep(void)
+{
+       unsigned long long      time_ns;
+       struct timespec         tv;
+
+       if (bg_mode < 2)
+               return;
+
+       time_ns =  100 * NSEC_PER_USEC * (bg_mode - 1);
+       tv.tv_sec = time_ns / NSEC_PER_SEC;
+       tv.tv_nsec = time_ns % NSEC_PER_SEC;
+       nanosleep(&tv, NULL);
+}
+
+/*
+ * Return the input string with non-printing bytes escaped.
+ * Caller must free the buffer.
+ */
+char *
+string_escape(
+       const char              *in)
+{
+       char                    *str;
+       const char              *p;
+       char                    *q;
+       int                     x;
+
+       str = malloc(strlen(in) * 4);
+       if (!str)
+               return NULL;
+       for (p = in, q = str; *p != '\0'; p++) {
+               if (isprint(*p)) {
+                       *q = *p;
+                       q++;
+               } else {
+                       x = sprintf(q, "\\x%02x", *p);
+                       q += x;
+               }
+       }
+       *q = '\0';
+       return str;
+}
+
+/*
+ * Record another naming warning, and decide if it's worth
+ * complaining about.
+ */
+bool
+should_warn_about_name(
+       struct scrub_ctx        *ctx)
+{
+       bool                    whine;
+       bool                    res;
+
+       pthread_mutex_lock(&ctx->lock);
+       ctx->naming_warnings++;
+       whine = ctx->naming_warnings == TOO_MANY_NAME_WARNINGS;
+       res = ctx->naming_warnings < TOO_MANY_NAME_WARNINGS;
        pthread_mutex_unlock(&ctx->lock);
+
+       if (whine && !(debug || verbose))
+               str_info(ctx, ctx->mntpoint,
+_("More than %u naming warnings, shutting up."),
+                               TOO_MANY_NAME_WARNINGS);
+
+       return debug || verbose || res;
+}
+
+/* Decide if a value is within +/- (n/d) of a desired value. */
+bool
+within_range(
+       struct scrub_ctx        *ctx,
+       unsigned long long      value,
+       unsigned long long      desired,
+       unsigned long long      abs_threshold,
+       unsigned int            n,
+       unsigned int            d,
+       const char              *descr)
+{
+       assert(n < d);
+
+       /* Don't complain if difference does not exceed an absolute value. */
+       if (value < desired && desired - value < abs_threshold)
+               return true;
+       if (value > desired && value - desired < abs_threshold)
+               return true;
+
+       /* Complain if the difference exceeds a certain percentage. */
+       if (value < desired * (d - n) / d)
+               return false;
+       if (value > desired * (d + n) / d)
+               return false;
+
+       return true;
+}
+
+/*
+ * Render an inode number into a buffer in a format suitable for use in
+ * log messages. The buffer will be filled with:
+ *     "inode <inode number> (<ag number>/<ag inode number>)"
+ * If the @format argument is non-NULL, it will be rendered into the buffer
+ * after the inode representation and a single space.
+ */
+int
+scrub_render_ino_descr(
+       const struct scrub_ctx  *ctx,
+       char                    *buf,
+       size_t                  buflen,
+       uint64_t                ino,
+       uint32_t                gen,
+       const char              *format,
+       ...)
+{
+       va_list                 args;
+       uint32_t                agno;
+       uint32_t                agino;
+       int                     ret;
+
+       agno = cvt_ino_to_agno(&ctx->mnt, ino);
+       agino = cvt_ino_to_agino(&ctx->mnt, ino);
+       ret = snprintf(buf, buflen, _("inode %"PRIu64" (%"PRIu32"/%"PRIu32")%s"),
+                       ino, agno, agino, format ? " " : "");
+       if (ret < 0 || ret >= buflen || format == NULL)
+               return ret;
+
+       va_start(args, format);
+       ret += vsnprintf(buf + ret, buflen - ret, format, args);
+       va_end(args);
+       return ret;
 }