]>
Commit | Line | Data |
---|---|---|
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" |
50a573a7 | 11 | #include "path.h" |
f0585fce | 12 | #include "xfs_scrub.h" |
95b1e505 | 13 | #include "common.h" |
ed60d210 | 14 | #include "progress.h" |
f0585fce | 15 | |
7c309151 DW |
16 | extern 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. */ | |
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 | ||
25992d9c ES |
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_REPAIR] = { .string = "Repaired", .loglevel = LOG_WARNING }, | |
52 | [S_INFO] = { .string = "Info", .loglevel = LOG_INFO }, | |
53 | [S_PREEN] = { .string = "Optimized", .loglevel = LOG_INFO } | |
7c309151 DW |
54 | }; |
55 | ||
ed60d210 DW |
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 | ||
f0585fce DW |
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); | |
19852474 DW |
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 | ||
25992d9c ES |
92 | fprintf(stream, "%s%s: %s: ", stream_start(stream), |
93 | _(err_levels[level].string), descr); | |
f0585fce DW |
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 | ||
19852474 | 108 | out_record: |
f0585fce DW |
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++; | |
19852474 DW |
115 | else if (level == S_REPAIR) |
116 | ctx->repairs++; | |
117 | else if (level == S_PREEN) | |
118 | ctx->preens++; | |
f0585fce DW |
119 | |
120 | pthread_mutex_unlock(&ctx->lock); | |
121 | } | |
173a0283 | 122 | |
7c309151 DW |
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 | ||
25992d9c | 154 | sz = snprintf(buf, LOG_BUFSZ, "%s: ", _(err_levels[level].string)); |
7c309151 DW |
155 | va_start(args, format); |
156 | vsnprintf(buf + sz, LOG_BUFSZ - sz, format, args); | |
157 | va_end(args); | |
25992d9c | 158 | syslog(err_levels[level].loglevel, "%s", buf); |
7c309151 DW |
159 | |
160 | closelog(); | |
161 | } | |
162 | ||
173a0283 DW |
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, | |
058f45da DW |
203 | char **units, |
204 | int *precision) | |
173a0283 DW |
205 | { |
206 | if (debug > 1) | |
207 | goto no_prefix; | |
058f45da | 208 | *precision = 1; |
173a0283 DW |
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 = ""; | |
058f45da | 225 | *precision = 0; |
173a0283 DW |
226 | return number; |
227 | } | |
e031d90f DW |
228 | |
229 | /* How many threads to kick off? */ | |
230 | unsigned int | |
231 | scrub_nproc( | |
232 | struct scrub_ctx *ctx) | |
233 | { | |
32c6cc09 DW |
234 | if (force_nr_threads) |
235 | return force_nr_threads; | |
e031d90f DW |
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 | } | |
50a573a7 | 254 | |
fd7d73c0 | 255 | /* |
1500ee2c | 256 | * Sleep for 100us * however many -b we got past the initial one. |
fd7d73c0 DW |
257 | * This is an (albeit clumsy) way to throttle scrub activity. |
258 | */ | |
1500ee2c DW |
259 | #define NSEC_PER_SEC 1000000000ULL |
260 | #define NSEC_PER_USEC 1000ULL | |
fd7d73c0 DW |
261 | void |
262 | background_sleep(void) | |
263 | { | |
1500ee2c | 264 | unsigned long long time_ns; |
fd7d73c0 DW |
265 | struct timespec tv; |
266 | ||
267 | if (bg_mode < 2) | |
268 | return; | |
269 | ||
1500ee2c DW |
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; | |
fd7d73c0 DW |
273 | nanosleep(&tv, NULL); |
274 | } | |
396cd022 DW |
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 | } | |
698c6c7c DW |
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 | } |