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