]>
git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - scrub/common.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2018-2024 Oracle. All Rights Reserved.
4 * Author: Darrick J. Wong <djwong@kernel.org>
8 #include <sys/statvfs.h>
10 #include "platform_defs.h"
11 #include "libfrog/paths.h"
12 #include "xfs_scrub.h"
16 extern char *progname
;
19 * Reporting Status to the Console
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.
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
31 /* Too many errors? Bail out. */
33 scrub_excessive_errors(
34 struct scrub_ctx
*ctx
)
36 unsigned long long errors_seen
;
39 * We only set max_errors at the start of the program, so it's safe to
40 * access it locklessly.
42 if (ctx
->max_errors
== 0)
45 pthread_mutex_lock(&ctx
->lock
);
46 errors_seen
= ctx
->corruptions_found
+ ctx
->unfixable_errors
;
47 pthread_mutex_unlock(&ctx
->lock
);
49 return errors_seen
>= ctx
->max_errors
;
61 .string
= "Corruption",
65 .string
= "Unfixable Error",
70 .loglevel
= LOG_WARNING
,
81 .string
= "Optimized",
86 /* If stream is a tty, clear to end of line to clean up progress bar. */
87 static inline const char *stream_start(FILE *stream
)
90 return stderr_isatty
? CLEAR_EOL
: "";
91 return stdout_isatty
? CLEAR_EOL
: "";
94 /* Print a warning string and some warning text. */
97 struct scrub_ctx
*ctx
,
99 enum error_level level
,
106 FILE *stream
= stderr
;
108 char buf
[DESCR_BUFSZ
];
110 /* print strerror or format of choice but not both */
111 assert(!(error
&& format
));
116 pthread_mutex_lock(&ctx
->lock
);
118 /* We only want to hear about optimizing when in debug/verbose mode. */
119 if (level
== S_PREEN
&& !debug
&& !verbose
)
122 fprintf(stream
, "%s%s: %s: ", stream_start(stream
),
123 _(err_levels
[level
].string
), descr
);
125 fprintf(stream
, _("%s."), strerror_r(error
, buf
, DESCR_BUFSZ
));
127 va_start(args
, format
);
128 vfprintf(stream
, format
, args
);
133 fprintf(stream
, _(" (%s line %d)"), file
, line
);
134 fprintf(stream
, "\n");
135 if (stream
== stdout
)
139 if (error
|| level
== S_ERROR
) /* A syscall failed */
140 ctx
->runtime_errors
++;
141 else if (level
== S_CORRUPT
)
142 ctx
->corruptions_found
++;
143 else if (level
== S_UNFIXABLE
)
144 ctx
->unfixable_errors
++;
145 else if (level
== S_WARN
)
146 ctx
->warnings_found
++;
147 else if (level
== S_REPAIR
)
149 else if (level
== S_PREEN
)
152 pthread_mutex_unlock(&ctx
->lock
);
155 /* Log a message to syslog. */
156 #define LOG_BUFSZ 4096
157 #define LOGNAME_BUFSZ 256
160 struct scrub_ctx
*ctx
,
161 enum error_level level
,
166 char logname
[LOGNAME_BUFSZ
];
170 /* We only want to hear about optimizing when in debug/verbose mode. */
171 if (level
== S_PREEN
&& !debug
&& !verbose
)
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
180 if (is_service
|| !isatty(fileno(stdin
)) || debug
)
183 snprintf(logname
, LOGNAME_BUFSZ
, "%s@%s", progname
, ctx
->mntpoint
);
184 openlog(logname
, LOG_PID
, LOG_DAEMON
);
186 sz
= snprintf(buf
, LOG_BUFSZ
, "%s: ", _(err_levels
[level
].string
));
187 va_start(args
, format
);
188 vsnprintf(buf
+ sz
, LOG_BUFSZ
- sz
, format
, args
);
190 syslog(err_levels
[level
].loglevel
, "%s", buf
);
200 return ((tv1
->tv_sec
- tv2
->tv_sec
) +
201 ((float) (tv1
->tv_usec
- tv2
->tv_usec
)) / 1000000);
204 /* Produce human readable disk space output. */
207 unsigned long long bytes
,
212 if (bytes
> (1ULL << 40)) {
214 return (double)bytes
/ (1ULL << 40);
215 } else if (bytes
> (1ULL << 30)) {
217 return (double)bytes
/ (1ULL << 30);
218 } else if (bytes
> (1ULL << 20)) {
220 return (double)bytes
/ (1ULL << 20);
221 } else if (bytes
> (1ULL << 10)) {
223 return (double)bytes
/ (1ULL << 10);
231 /* Produce human readable discrete number output. */
234 unsigned long long number
,
241 if (number
> 1000000000000ULL) {
243 return number
/ 1000000000000.0;
244 } else if (number
> 1000000000ULL) {
246 return number
/ 1000000000.0;
247 } else if (number
> 1000000ULL) {
249 return number
/ 1000000.0;
250 } else if (number
> 1000ULL) {
252 return number
/ 1000.0;
261 /* How many threads to kick off? */
264 struct scrub_ctx
*ctx
)
266 if (force_nr_threads
)
267 return force_nr_threads
;
268 return ctx
->nr_io_threads
;
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.
276 scrub_nproc_workqueue(
277 struct scrub_ctx
*ctx
)
281 x
= scrub_nproc(ctx
);
288 * Sleep for 100us * however many -b we got past the initial one.
289 * This is an (albeit clumsy) way to throttle scrub activity.
292 background_sleep(void)
294 unsigned long long time_ns
;
300 time_ns
= 100 * NSEC_PER_USEC
* (bg_mode
- 1);
301 tv
.tv_sec
= time_ns
/ NSEC_PER_SEC
;
302 tv
.tv_nsec
= time_ns
% NSEC_PER_SEC
;
303 nanosleep(&tv
, NULL
);
307 * Return the input string with non-printing bytes escaped.
308 * Caller must free the buffer.
319 str
= malloc(strlen(in
) * 4);
322 for (p
= in
, q
= str
; *p
!= '\0'; p
++) {
327 x
= sprintf(q
, "\\x%02x", *p
);
336 * Record another naming warning, and decide if it's worth
340 should_warn_about_name(
341 struct scrub_ctx
*ctx
)
346 pthread_mutex_lock(&ctx
->lock
);
347 ctx
->naming_warnings
++;
348 whine
= ctx
->naming_warnings
== TOO_MANY_NAME_WARNINGS
;
349 res
= ctx
->naming_warnings
< TOO_MANY_NAME_WARNINGS
;
350 pthread_mutex_unlock(&ctx
->lock
);
352 if (whine
&& !(debug
|| verbose
))
353 str_info(ctx
, ctx
->mntpoint
,
354 _("More than %u naming warnings, shutting up."),
355 TOO_MANY_NAME_WARNINGS
);
357 return debug
|| verbose
|| res
;
360 /* Decide if a value is within +/- (n/d) of a desired value. */
363 struct scrub_ctx
*ctx
,
364 unsigned long long value
,
365 unsigned long long desired
,
366 unsigned long long abs_threshold
,
373 /* Don't complain if difference does not exceed an absolute value. */
374 if (value
< desired
&& desired
- value
< abs_threshold
)
376 if (value
> desired
&& value
- desired
< abs_threshold
)
379 /* Complain if the difference exceeds a certain percentage. */
380 if (value
< desired
* (d
- n
) / d
)
382 if (value
> desired
* (d
+ n
) / d
)
389 * Render an inode number into a buffer in a format suitable for use in
390 * log messages. The buffer will be filled with:
391 * "inode <inode number> (<ag number>/<ag inode number>)"
392 * If the @format argument is non-NULL, it will be rendered into the buffer
393 * after the inode representation and a single space.
396 scrub_render_ino_descr(
397 const struct scrub_ctx
*ctx
,
410 agno
= cvt_ino_to_agno(&ctx
->mnt
, ino
);
411 agino
= cvt_ino_to_agino(&ctx
->mnt
, ino
);
412 ret
= snprintf(buf
, buflen
, _("inode %"PRIu64
" (%"PRIu32
"/%"PRIu32
")%s"),
413 ino
, agno
, agino
, format
? " " : "");
414 if (ret
< 0 || ret
>= buflen
|| format
== NULL
)
417 va_start(args
, format
);
418 ret
+= vsnprintf(buf
+ ret
, buflen
- ret
, format
, args
);