]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/common.c
xfs_scrub: clean up error level table
[thirdparty/xfsprogs-dev.git] / scrub / common.c
CommitLineData
959ef981 1// SPDX-License-Identifier: GPL-2.0+
95b1e505
DW
2/*
3 * Copyright (C) 2018 Oracle. All Rights Reserved.
95b1e505 4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
95b1e505 5 */
a440f877 6#include "xfs.h"
f0585fce 7#include <pthread.h>
50a573a7 8#include <sys/statvfs.h>
7c309151 9#include <syslog.h>
f0585fce 10#include "platform_defs.h"
42b4c8e8 11#include "libfrog/paths.h"
f0585fce 12#include "xfs_scrub.h"
95b1e505 13#include "common.h"
ed60d210 14#include "progress.h"
f0585fce 15
7c309151
DW
16extern char *progname;
17
f0585fce
DW
18/*
19 * Reporting Status to the Console
20 *
21 * We aim for a roughly standard reporting format -- the severity of the
22 * status being reported, a textual description of the object being
23 * reported, and whatever the status happens to be.
24 *
25 * Errors are the most severe and reflect filesystem corruption.
26 * Warnings indicate that something is amiss and needs the attention of
27 * the administrator, but does not constitute a corruption. Information
28 * is merely advisory.
29 */
30
31/* Too many errors? Bail out. */
32bool
33xfs_scrub_excessive_errors(
34 struct scrub_ctx *ctx)
35{
36 bool ret;
37
38 pthread_mutex_lock(&ctx->lock);
39 ret = ctx->max_errors > 0 && ctx->errors_found >= ctx->max_errors;
40 pthread_mutex_unlock(&ctx->lock);
41
42 return ret;
43}
44
25992d9c
ES
45static struct {
46 const char *string;
47 int loglevel;
48} err_levels[] = {
420fad2d
DW
49 [S_ERROR] = {
50 .string = "Error",
51 .loglevel = LOG_ERR,
52 },
53 [S_WARN] = {
54 .string = "Warning",
55 .loglevel = LOG_WARNING,
56 },
57 [S_INFO] = {
58 .string = "Info",
59 .loglevel = LOG_INFO,
60 },
61 [S_REPAIR] = {
62 .string = "Repaired",
63 .loglevel = LOG_INFO,
64 },
65 [S_PREEN] = {
66 .string = "Optimized",
67 .loglevel = LOG_INFO,
68 },
7c309151
DW
69};
70
ed60d210
DW
71/* If stream is a tty, clear to end of line to clean up progress bar. */
72static inline const char *stream_start(FILE *stream)
73{
74 if (stream == stderr)
75 return stderr_isatty ? CLEAR_EOL : "";
76 return stdout_isatty ? CLEAR_EOL : "";
77}
78
f0585fce
DW
79/* Print a warning string and some warning text. */
80void
81__str_out(
82 struct scrub_ctx *ctx,
83 const char *descr,
84 enum error_level level,
85 int error,
86 const char *file,
87 int line,
88 const char *format,
89 ...)
90{
91 FILE *stream = stderr;
92 va_list args;
93 char buf[DESCR_BUFSZ];
94
95 /* print strerror or format of choice but not both */
96 assert(!(error && format));
97
98 if (level >= S_INFO)
99 stream = stdout;
100
101 pthread_mutex_lock(&ctx->lock);
19852474
DW
102
103 /* We only want to hear about optimizing when in debug/verbose mode. */
104 if (level == S_PREEN && !debug && !verbose)
105 goto out_record;
106
25992d9c
ES
107 fprintf(stream, "%s%s: %s: ", stream_start(stream),
108 _(err_levels[level].string), descr);
f0585fce
DW
109 if (error) {
110 fprintf(stream, _("%s."), strerror_r(error, buf, DESCR_BUFSZ));
111 } else {
112 va_start(args, format);
113 vfprintf(stream, format, args);
114 va_end(args);
115 }
116
117 if (debug)
118 fprintf(stream, _(" (%s line %d)"), file, line);
119 fprintf(stream, "\n");
120 if (stream == stdout)
121 fflush(stream);
122
19852474 123out_record:
f0585fce
DW
124 if (error) /* A syscall failed */
125 ctx->runtime_errors++;
126 else if (level == S_ERROR)
127 ctx->errors_found++;
128 else if (level == S_WARN)
129 ctx->warnings_found++;
19852474
DW
130 else if (level == S_REPAIR)
131 ctx->repairs++;
132 else if (level == S_PREEN)
133 ctx->preens++;
f0585fce
DW
134
135 pthread_mutex_unlock(&ctx->lock);
136}
173a0283 137
7c309151
DW
138/* Log a message to syslog. */
139#define LOG_BUFSZ 4096
140#define LOGNAME_BUFSZ 256
141void
142__str_log(
143 struct scrub_ctx *ctx,
144 enum error_level level,
145 const char *format,
146 ...)
147{
148 va_list args;
149 char logname[LOGNAME_BUFSZ];
150 char buf[LOG_BUFSZ];
151 int sz;
152
153 /* We only want to hear about optimizing when in debug/verbose mode. */
154 if (level == S_PREEN && !debug && !verbose)
155 return;
156
157 /*
158 * Skip logging if we're being run as a service (presumably the
159 * service will log stdout/stderr); if we're being run in a non
160 * interactive manner (assume we're a service); or if we're in
161 * debug mode.
162 */
163 if (is_service || !isatty(fileno(stdin)) || debug)
164 return;
165
166 snprintf(logname, LOGNAME_BUFSZ, "%s@%s", progname, ctx->mntpoint);
167 openlog(logname, LOG_PID, LOG_DAEMON);
168
25992d9c 169 sz = snprintf(buf, LOG_BUFSZ, "%s: ", _(err_levels[level].string));
7c309151
DW
170 va_start(args, format);
171 vsnprintf(buf + sz, LOG_BUFSZ - sz, format, args);
172 va_end(args);
25992d9c 173 syslog(err_levels[level].loglevel, "%s", buf);
7c309151
DW
174
175 closelog();
176}
177
173a0283
DW
178double
179timeval_subtract(
180 struct timeval *tv1,
181 struct timeval *tv2)
182{
183 return ((tv1->tv_sec - tv2->tv_sec) +
184 ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
185}
186
187/* Produce human readable disk space output. */
188double
189auto_space_units(
190 unsigned long long bytes,
191 char **units)
192{
193 if (debug > 1)
194 goto no_prefix;
195 if (bytes > (1ULL << 40)) {
196 *units = "TiB";
197 return (double)bytes / (1ULL << 40);
198 } else if (bytes > (1ULL << 30)) {
199 *units = "GiB";
200 return (double)bytes / (1ULL << 30);
201 } else if (bytes > (1ULL << 20)) {
202 *units = "MiB";
203 return (double)bytes / (1ULL << 20);
204 } else if (bytes > (1ULL << 10)) {
205 *units = "KiB";
206 return (double)bytes / (1ULL << 10);
207 }
208
209no_prefix:
210 *units = "B";
211 return bytes;
212}
213
214/* Produce human readable discrete number output. */
215double
216auto_units(
217 unsigned long long number,
058f45da
DW
218 char **units,
219 int *precision)
173a0283
DW
220{
221 if (debug > 1)
222 goto no_prefix;
058f45da 223 *precision = 1;
173a0283
DW
224 if (number > 1000000000000ULL) {
225 *units = "T";
226 return number / 1000000000000.0;
227 } else if (number > 1000000000ULL) {
228 *units = "G";
229 return number / 1000000000.0;
230 } else if (number > 1000000ULL) {
231 *units = "M";
232 return number / 1000000.0;
233 } else if (number > 1000ULL) {
234 *units = "K";
235 return number / 1000.0;
236 }
237
238no_prefix:
239 *units = "";
058f45da 240 *precision = 0;
173a0283
DW
241 return number;
242}
e031d90f
DW
243
244/* How many threads to kick off? */
245unsigned int
246scrub_nproc(
247 struct scrub_ctx *ctx)
248{
32c6cc09
DW
249 if (force_nr_threads)
250 return force_nr_threads;
e031d90f
DW
251 return ctx->nr_io_threads;
252}
253
254/*
255 * How many threads to kick off for a workqueue? If we only want one
256 * thread, save ourselves the overhead and just run it in the main thread.
257 */
258unsigned int
259scrub_nproc_workqueue(
260 struct scrub_ctx *ctx)
261{
262 unsigned int x;
263
264 x = scrub_nproc(ctx);
265 if (x == 1)
266 x = 0;
267 return x;
268}
50a573a7 269
fd7d73c0 270/*
1500ee2c 271 * Sleep for 100us * however many -b we got past the initial one.
fd7d73c0
DW
272 * This is an (albeit clumsy) way to throttle scrub activity.
273 */
1500ee2c
DW
274#define NSEC_PER_SEC 1000000000ULL
275#define NSEC_PER_USEC 1000ULL
fd7d73c0
DW
276void
277background_sleep(void)
278{
1500ee2c 279 unsigned long long time_ns;
fd7d73c0
DW
280 struct timespec tv;
281
282 if (bg_mode < 2)
283 return;
284
1500ee2c
DW
285 time_ns = 100 * NSEC_PER_USEC * (bg_mode - 1);
286 tv.tv_sec = time_ns / NSEC_PER_SEC;
287 tv.tv_nsec = time_ns % NSEC_PER_SEC;
fd7d73c0
DW
288 nanosleep(&tv, NULL);
289}
396cd022
DW
290
291/*
292 * Return the input string with non-printing bytes escaped.
293 * Caller must free the buffer.
294 */
295char *
296string_escape(
297 const char *in)
298{
299 char *str;
300 const char *p;
301 char *q;
302 int x;
303
304 str = malloc(strlen(in) * 4);
305 if (!str)
306 return NULL;
307 for (p = in, q = str; *p != '\0'; p++) {
308 if (isprint(*p)) {
309 *q = *p;
310 q++;
311 } else {
312 x = sprintf(q, "\\x%02x", *p);
313 q += x;
314 }
315 }
316 *q = '\0';
317 return str;
318}
319
320/*
321 * Record another naming warning, and decide if it's worth
322 * complaining about.
323 */
324bool
325should_warn_about_name(
326 struct scrub_ctx *ctx)
327{
328 bool whine;
329 bool res;
330
331 pthread_mutex_lock(&ctx->lock);
332 ctx->naming_warnings++;
333 whine = ctx->naming_warnings == TOO_MANY_NAME_WARNINGS;
334 res = ctx->naming_warnings < TOO_MANY_NAME_WARNINGS;
335 pthread_mutex_unlock(&ctx->lock);
336
337 if (whine && !(debug || verbose))
338 str_info(ctx, ctx->mntpoint,
339_("More than %u naming warnings, shutting up."),
340 TOO_MANY_NAME_WARNINGS);
341
342 return debug || verbose || res;
343}
698c6c7c
DW
344
345/* Decide if a value is within +/- (n/d) of a desired value. */
346bool
347within_range(
348 struct scrub_ctx *ctx,
349 unsigned long long value,
350 unsigned long long desired,
351 unsigned long long abs_threshold,
352 unsigned int n,
353 unsigned int d,
354 const char *descr)
355{
356 assert(n < d);
357
358 /* Don't complain if difference does not exceed an absolute value. */
359 if (value < desired && desired - value < abs_threshold)
360 return true;
361 if (value > desired && value - desired < abs_threshold)
362 return true;
363
364 /* Complain if the difference exceeds a certain percentage. */
365 if (value < desired * (d - n) / d)
366 return false;
367 if (value > desired * (d + n) / d)
368 return false;
369
370 return true;
371}
15589f0a
DW
372
373/*
374 * Render an inode number into a buffer in a format suitable for use in
375 * log messages. The buffer will be filled with:
376 * "inode <inode number> (<ag number>/<ag inode number>)"
377 * If the @format argument is non-NULL, it will be rendered into the buffer
378 * after the inode representation and a single space.
379 */
380int
381scrub_render_ino_descr(
382 const struct scrub_ctx *ctx,
383 char *buf,
384 size_t buflen,
385 uint64_t ino,
386 uint32_t gen,
387 const char *format,
388 ...)
389{
390 va_list args;
391 uint32_t agno;
392 uint32_t agino;
393 int ret;
394
395 agno = cvt_ino_to_agno(&ctx->mnt, ino);
396 agino = cvt_ino_to_agino(&ctx->mnt, ino);
397 ret = snprintf(buf, buflen, _("inode %"PRIu64" (%"PRIu32"/%"PRIu32")%s"),
398 ino, agno, agino, format ? " " : "");
399 if (ret < 0 || ret >= buflen || format == NULL)
400 return ret;
401
402 va_start(args, format);
403 ret += vsnprintf(buf + ret, buflen - ret, format, args);
404 va_end(args);
405 return ret;
406}