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