]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/common.c
xfs_scrub: fix #include ordering to avoid build failure
[thirdparty/xfsprogs-dev.git] / scrub / common.c
CommitLineData
95b1e505
DW
1/*
2 * Copyright (C) 2018 Oracle. All Rights Reserved.
3 *
4 * Author: Darrick J. Wong <darrick.wong@oracle.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it would be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
a440f877 20#include "xfs.h"
f0585fce 21#include <pthread.h>
50a573a7 22#include <sys/statvfs.h>
7c309151 23#include <syslog.h>
f0585fce 24#include "platform_defs.h"
50a573a7 25#include "path.h"
f0585fce 26#include "xfs_scrub.h"
95b1e505 27#include "common.h"
ed60d210 28#include "progress.h"
f0585fce 29
7c309151
DW
30extern char *progname;
31
f0585fce
DW
32/*
33 * Reporting Status to the Console
34 *
35 * We aim for a roughly standard reporting format -- the severity of the
36 * status being reported, a textual description of the object being
37 * reported, and whatever the status happens to be.
38 *
39 * Errors are the most severe and reflect filesystem corruption.
40 * Warnings indicate that something is amiss and needs the attention of
41 * the administrator, but does not constitute a corruption. Information
42 * is merely advisory.
43 */
44
45/* Too many errors? Bail out. */
46bool
47xfs_scrub_excessive_errors(
48 struct scrub_ctx *ctx)
49{
50 bool ret;
51
52 pthread_mutex_lock(&ctx->lock);
53 ret = ctx->max_errors > 0 && ctx->errors_found >= ctx->max_errors;
54 pthread_mutex_unlock(&ctx->lock);
55
56 return ret;
57}
58
59static const char *err_str[] = {
60 [S_ERROR] = "Error",
61 [S_WARN] = "Warning",
19852474 62 [S_REPAIR] = "Repaired",
f0585fce 63 [S_INFO] = "Info",
19852474 64 [S_PREEN] = "Optimized",
f0585fce
DW
65};
66
7c309151
DW
67static int log_level[] = {
68 [S_ERROR] = LOG_ERR,
69 [S_WARN] = LOG_WARNING,
70 [S_INFO] = LOG_INFO,
71};
72
ed60d210
DW
73/* If stream is a tty, clear to end of line to clean up progress bar. */
74static inline const char *stream_start(FILE *stream)
75{
76 if (stream == stderr)
77 return stderr_isatty ? CLEAR_EOL : "";
78 return stdout_isatty ? CLEAR_EOL : "";
79}
80
f0585fce
DW
81/* Print a warning string and some warning text. */
82void
83__str_out(
84 struct scrub_ctx *ctx,
85 const char *descr,
86 enum error_level level,
87 int error,
88 const char *file,
89 int line,
90 const char *format,
91 ...)
92{
93 FILE *stream = stderr;
94 va_list args;
95 char buf[DESCR_BUFSZ];
96
97 /* print strerror or format of choice but not both */
98 assert(!(error && format));
99
100 if (level >= S_INFO)
101 stream = stdout;
102
103 pthread_mutex_lock(&ctx->lock);
19852474
DW
104
105 /* We only want to hear about optimizing when in debug/verbose mode. */
106 if (level == S_PREEN && !debug && !verbose)
107 goto out_record;
108
ed60d210
DW
109 fprintf(stream, "%s%s: %s: ", stream_start(stream), _(err_str[level]),
110 descr);
f0585fce
DW
111 if (error) {
112 fprintf(stream, _("%s."), strerror_r(error, buf, DESCR_BUFSZ));
113 } else {
114 va_start(args, format);
115 vfprintf(stream, format, args);
116 va_end(args);
117 }
118
119 if (debug)
120 fprintf(stream, _(" (%s line %d)"), file, line);
121 fprintf(stream, "\n");
122 if (stream == stdout)
123 fflush(stream);
124
19852474 125out_record:
f0585fce
DW
126 if (error) /* A syscall failed */
127 ctx->runtime_errors++;
128 else if (level == S_ERROR)
129 ctx->errors_found++;
130 else if (level == S_WARN)
131 ctx->warnings_found++;
19852474
DW
132 else if (level == S_REPAIR)
133 ctx->repairs++;
134 else if (level == S_PREEN)
135 ctx->preens++;
f0585fce
DW
136
137 pthread_mutex_unlock(&ctx->lock);
138}
173a0283 139
7c309151
DW
140/* Log a message to syslog. */
141#define LOG_BUFSZ 4096
142#define LOGNAME_BUFSZ 256
143void
144__str_log(
145 struct scrub_ctx *ctx,
146 enum error_level level,
147 const char *format,
148 ...)
149{
150 va_list args;
151 char logname[LOGNAME_BUFSZ];
152 char buf[LOG_BUFSZ];
153 int sz;
154
155 /* We only want to hear about optimizing when in debug/verbose mode. */
156 if (level == S_PREEN && !debug && !verbose)
157 return;
158
159 /*
160 * Skip logging if we're being run as a service (presumably the
161 * service will log stdout/stderr); if we're being run in a non
162 * interactive manner (assume we're a service); or if we're in
163 * debug mode.
164 */
165 if (is_service || !isatty(fileno(stdin)) || debug)
166 return;
167
168 snprintf(logname, LOGNAME_BUFSZ, "%s@%s", progname, ctx->mntpoint);
169 openlog(logname, LOG_PID, LOG_DAEMON);
170
171 sz = snprintf(buf, LOG_BUFSZ, "%s: ", _(err_str[level]));
172 va_start(args, format);
173 vsnprintf(buf + sz, LOG_BUFSZ - sz, format, args);
174 va_end(args);
175 syslog(log_level[level], "%s", buf);
176
177 closelog();
178}
179
173a0283
DW
180double
181timeval_subtract(
182 struct timeval *tv1,
183 struct timeval *tv2)
184{
185 return ((tv1->tv_sec - tv2->tv_sec) +
186 ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
187}
188
189/* Produce human readable disk space output. */
190double
191auto_space_units(
192 unsigned long long bytes,
193 char **units)
194{
195 if (debug > 1)
196 goto no_prefix;
197 if (bytes > (1ULL << 40)) {
198 *units = "TiB";
199 return (double)bytes / (1ULL << 40);
200 } else if (bytes > (1ULL << 30)) {
201 *units = "GiB";
202 return (double)bytes / (1ULL << 30);
203 } else if (bytes > (1ULL << 20)) {
204 *units = "MiB";
205 return (double)bytes / (1ULL << 20);
206 } else if (bytes > (1ULL << 10)) {
207 *units = "KiB";
208 return (double)bytes / (1ULL << 10);
209 }
210
211no_prefix:
212 *units = "B";
213 return bytes;
214}
215
216/* Produce human readable discrete number output. */
217double
218auto_units(
219 unsigned long long number,
220 char **units)
221{
222 if (debug > 1)
223 goto no_prefix;
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 = "";
240 return number;
241}
e031d90f
DW
242
243/* How many threads to kick off? */
244unsigned int
245scrub_nproc(
246 struct scrub_ctx *ctx)
247{
248 if (nr_threads)
249 return nr_threads;
250 return ctx->nr_io_threads;
251}
252
253/*
254 * How many threads to kick off for a workqueue? If we only want one
255 * thread, save ourselves the overhead and just run it in the main thread.
256 */
257unsigned int
258scrub_nproc_workqueue(
259 struct scrub_ctx *ctx)
260{
261 unsigned int x;
262
263 x = scrub_nproc(ctx);
264 if (x == 1)
265 x = 0;
266 return x;
267}
50a573a7
DW
268
269/*
270 * Check if the argument is either the device name or mountpoint of a mounted
271 * filesystem.
272 */
273#define MNTTYPE_XFS "xfs"
274static bool
275find_mountpoint_check(
276 struct stat *sb,
277 struct mntent *t)
278{
279 struct stat ms;
280
281 if (S_ISDIR(sb->st_mode)) { /* mount point */
282 if (stat(t->mnt_dir, &ms) < 0)
283 return false;
284 if (sb->st_ino != ms.st_ino)
285 return false;
286 if (sb->st_dev != ms.st_dev)
287 return false;
288 if (strcmp(t->mnt_type, MNTTYPE_XFS) != 0)
289 return NULL;
290 } else { /* device */
291 if (stat(t->mnt_fsname, &ms) < 0)
292 return false;
293 if (sb->st_rdev != ms.st_rdev)
294 return false;
295 if (strcmp(t->mnt_type, MNTTYPE_XFS) != 0)
296 return NULL;
297 /*
298 * Make sure the mountpoint given by mtab is accessible
299 * before using it.
300 */
301 if (stat(t->mnt_dir, &ms) < 0)
302 return false;
303 }
304
305 return true;
306}
307
308/* Check that our alleged mountpoint is in mtab */
309bool
310find_mountpoint(
311 char *mtab,
312 struct scrub_ctx *ctx)
313{
314 struct mntent_cursor cursor;
315 struct mntent *t = NULL;
316 bool found = false;
317
318 if (platform_mntent_open(&cursor, mtab) != 0) {
319 fprintf(stderr, "Error: can't get mntent entries.\n");
320 exit(1);
321 }
322
323 while ((t = platform_mntent_next(&cursor)) != NULL) {
324 /*
325 * Keep jotting down matching mount details; newer mounts are
326 * towards the end of the file (hopefully).
327 */
328 if (find_mountpoint_check(&ctx->mnt_sb, t)) {
329 ctx->mntpoint = strdup(t->mnt_dir);
330 ctx->blkdev = strdup(t->mnt_fsname);
331 found = true;
332 }
333 }
334 platform_mntent_close(&cursor);
335 return found;
336}
fd7d73c0
DW
337
338/*
339 * Sleep for 100ms * however many -b we got past the initial one.
340 * This is an (albeit clumsy) way to throttle scrub activity.
341 */
342void
343background_sleep(void)
344{
345 unsigned long long time;
346 struct timespec tv;
347
348 if (bg_mode < 2)
349 return;
350
351 time = 100000ULL * (bg_mode - 1);
352 tv.tv_sec = time / 1000000;
353 tv.tv_nsec = time % 1000000;
354 nanosleep(&tv, NULL);
355}
396cd022
DW
356
357/*
358 * Return the input string with non-printing bytes escaped.
359 * Caller must free the buffer.
360 */
361char *
362string_escape(
363 const char *in)
364{
365 char *str;
366 const char *p;
367 char *q;
368 int x;
369
370 str = malloc(strlen(in) * 4);
371 if (!str)
372 return NULL;
373 for (p = in, q = str; *p != '\0'; p++) {
374 if (isprint(*p)) {
375 *q = *p;
376 q++;
377 } else {
378 x = sprintf(q, "\\x%02x", *p);
379 q += x;
380 }
381 }
382 *q = '\0';
383 return str;
384}
385
386/*
387 * Record another naming warning, and decide if it's worth
388 * complaining about.
389 */
390bool
391should_warn_about_name(
392 struct scrub_ctx *ctx)
393{
394 bool whine;
395 bool res;
396
397 pthread_mutex_lock(&ctx->lock);
398 ctx->naming_warnings++;
399 whine = ctx->naming_warnings == TOO_MANY_NAME_WARNINGS;
400 res = ctx->naming_warnings < TOO_MANY_NAME_WARNINGS;
401 pthread_mutex_unlock(&ctx->lock);
402
403 if (whine && !(debug || verbose))
404 str_info(ctx, ctx->mntpoint,
405_("More than %u naming warnings, shutting up."),
406 TOO_MANY_NAME_WARNINGS);
407
408 return debug || verbose || res;
409}
698c6c7c
DW
410
411/* Decide if a value is within +/- (n/d) of a desired value. */
412bool
413within_range(
414 struct scrub_ctx *ctx,
415 unsigned long long value,
416 unsigned long long desired,
417 unsigned long long abs_threshold,
418 unsigned int n,
419 unsigned int d,
420 const char *descr)
421{
422 assert(n < d);
423
424 /* Don't complain if difference does not exceed an absolute value. */
425 if (value < desired && desired - value < abs_threshold)
426 return true;
427 if (value > desired && value - desired < abs_threshold)
428 return true;
429
430 /* Complain if the difference exceeds a certain percentage. */
431 if (value < desired * (d - n) / d)
432 return false;
433 if (value > desired * (d + n) / d)
434 return false;
435
436 return true;
437}