]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - scrub/common.c
xfs_scrub: optionally use SCSI READ VERIFY commands to scrub data blocks on disk
[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 */
f0585fce
DW
20#include <stdio.h>
21#include <pthread.h>
22#include <stdbool.h>
50a573a7 23#include <sys/statvfs.h>
f0585fce
DW
24#include "platform_defs.h"
25#include "xfs.h"
50a573a7
DW
26#include "xfs_fs.h"
27#include "path.h"
f0585fce 28#include "xfs_scrub.h"
95b1e505 29#include "common.h"
f0585fce
DW
30
31/*
32 * Reporting Status to the Console
33 *
34 * We aim for a roughly standard reporting format -- the severity of the
35 * status being reported, a textual description of the object being
36 * reported, and whatever the status happens to be.
37 *
38 * Errors are the most severe and reflect filesystem corruption.
39 * Warnings indicate that something is amiss and needs the attention of
40 * the administrator, but does not constitute a corruption. Information
41 * is merely advisory.
42 */
43
44/* Too many errors? Bail out. */
45bool
46xfs_scrub_excessive_errors(
47 struct scrub_ctx *ctx)
48{
49 bool ret;
50
51 pthread_mutex_lock(&ctx->lock);
52 ret = ctx->max_errors > 0 && ctx->errors_found >= ctx->max_errors;
53 pthread_mutex_unlock(&ctx->lock);
54
55 return ret;
56}
57
58static const char *err_str[] = {
59 [S_ERROR] = "Error",
60 [S_WARN] = "Warning",
61 [S_INFO] = "Info",
62};
63
64/* Print a warning string and some warning text. */
65void
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 fprintf(stream, "%s: %s: ", _(err_str[level]), descr);
88 if (error) {
89 fprintf(stream, _("%s."), strerror_r(error, buf, DESCR_BUFSZ));
90 } else {
91 va_start(args, format);
92 vfprintf(stream, format, args);
93 va_end(args);
94 }
95
96 if (debug)
97 fprintf(stream, _(" (%s line %d)"), file, line);
98 fprintf(stream, "\n");
99 if (stream == stdout)
100 fflush(stream);
101
102 if (error) /* A syscall failed */
103 ctx->runtime_errors++;
104 else if (level == S_ERROR)
105 ctx->errors_found++;
106 else if (level == S_WARN)
107 ctx->warnings_found++;
108
109 pthread_mutex_unlock(&ctx->lock);
110}
173a0283
DW
111
112double
113timeval_subtract(
114 struct timeval *tv1,
115 struct timeval *tv2)
116{
117 return ((tv1->tv_sec - tv2->tv_sec) +
118 ((float) (tv1->tv_usec - tv2->tv_usec)) / 1000000);
119}
120
121/* Produce human readable disk space output. */
122double
123auto_space_units(
124 unsigned long long bytes,
125 char **units)
126{
127 if (debug > 1)
128 goto no_prefix;
129 if (bytes > (1ULL << 40)) {
130 *units = "TiB";
131 return (double)bytes / (1ULL << 40);
132 } else if (bytes > (1ULL << 30)) {
133 *units = "GiB";
134 return (double)bytes / (1ULL << 30);
135 } else if (bytes > (1ULL << 20)) {
136 *units = "MiB";
137 return (double)bytes / (1ULL << 20);
138 } else if (bytes > (1ULL << 10)) {
139 *units = "KiB";
140 return (double)bytes / (1ULL << 10);
141 }
142
143no_prefix:
144 *units = "B";
145 return bytes;
146}
147
148/* Produce human readable discrete number output. */
149double
150auto_units(
151 unsigned long long number,
152 char **units)
153{
154 if (debug > 1)
155 goto no_prefix;
156 if (number > 1000000000000ULL) {
157 *units = "T";
158 return number / 1000000000000.0;
159 } else if (number > 1000000000ULL) {
160 *units = "G";
161 return number / 1000000000.0;
162 } else if (number > 1000000ULL) {
163 *units = "M";
164 return number / 1000000.0;
165 } else if (number > 1000ULL) {
166 *units = "K";
167 return number / 1000.0;
168 }
169
170no_prefix:
171 *units = "";
172 return number;
173}
e031d90f
DW
174
175/* How many threads to kick off? */
176unsigned int
177scrub_nproc(
178 struct scrub_ctx *ctx)
179{
180 if (nr_threads)
181 return nr_threads;
182 return ctx->nr_io_threads;
183}
184
185/*
186 * How many threads to kick off for a workqueue? If we only want one
187 * thread, save ourselves the overhead and just run it in the main thread.
188 */
189unsigned int
190scrub_nproc_workqueue(
191 struct scrub_ctx *ctx)
192{
193 unsigned int x;
194
195 x = scrub_nproc(ctx);
196 if (x == 1)
197 x = 0;
198 return x;
199}
50a573a7
DW
200
201/*
202 * Check if the argument is either the device name or mountpoint of a mounted
203 * filesystem.
204 */
205#define MNTTYPE_XFS "xfs"
206static bool
207find_mountpoint_check(
208 struct stat *sb,
209 struct mntent *t)
210{
211 struct stat ms;
212
213 if (S_ISDIR(sb->st_mode)) { /* mount point */
214 if (stat(t->mnt_dir, &ms) < 0)
215 return false;
216 if (sb->st_ino != ms.st_ino)
217 return false;
218 if (sb->st_dev != ms.st_dev)
219 return false;
220 if (strcmp(t->mnt_type, MNTTYPE_XFS) != 0)
221 return NULL;
222 } else { /* device */
223 if (stat(t->mnt_fsname, &ms) < 0)
224 return false;
225 if (sb->st_rdev != ms.st_rdev)
226 return false;
227 if (strcmp(t->mnt_type, MNTTYPE_XFS) != 0)
228 return NULL;
229 /*
230 * Make sure the mountpoint given by mtab is accessible
231 * before using it.
232 */
233 if (stat(t->mnt_dir, &ms) < 0)
234 return false;
235 }
236
237 return true;
238}
239
240/* Check that our alleged mountpoint is in mtab */
241bool
242find_mountpoint(
243 char *mtab,
244 struct scrub_ctx *ctx)
245{
246 struct mntent_cursor cursor;
247 struct mntent *t = NULL;
248 bool found = false;
249
250 if (platform_mntent_open(&cursor, mtab) != 0) {
251 fprintf(stderr, "Error: can't get mntent entries.\n");
252 exit(1);
253 }
254
255 while ((t = platform_mntent_next(&cursor)) != NULL) {
256 /*
257 * Keep jotting down matching mount details; newer mounts are
258 * towards the end of the file (hopefully).
259 */
260 if (find_mountpoint_check(&ctx->mnt_sb, t)) {
261 ctx->mntpoint = strdup(t->mnt_dir);
262 ctx->blkdev = strdup(t->mnt_fsname);
263 found = true;
264 }
265 }
266 platform_mntent_close(&cursor);
267 return found;
268}
fd7d73c0
DW
269
270/*
271 * Sleep for 100ms * however many -b we got past the initial one.
272 * This is an (albeit clumsy) way to throttle scrub activity.
273 */
274void
275background_sleep(void)
276{
277 unsigned long long time;
278 struct timespec tv;
279
280 if (bg_mode < 2)
281 return;
282
283 time = 100000ULL * (bg_mode - 1);
284 tv.tv_sec = time / 1000000;
285 tv.tv_nsec = time % 1000000;
286 nanosleep(&tv, NULL);
287}
396cd022
DW
288
289/*
290 * Return the input string with non-printing bytes escaped.
291 * Caller must free the buffer.
292 */
293char *
294string_escape(
295 const char *in)
296{
297 char *str;
298 const char *p;
299 char *q;
300 int x;
301
302 str = malloc(strlen(in) * 4);
303 if (!str)
304 return NULL;
305 for (p = in, q = str; *p != '\0'; p++) {
306 if (isprint(*p)) {
307 *q = *p;
308 q++;
309 } else {
310 x = sprintf(q, "\\x%02x", *p);
311 q += x;
312 }
313 }
314 *q = '\0';
315 return str;
316}
317
318/*
319 * Record another naming warning, and decide if it's worth
320 * complaining about.
321 */
322bool
323should_warn_about_name(
324 struct scrub_ctx *ctx)
325{
326 bool whine;
327 bool res;
328
329 pthread_mutex_lock(&ctx->lock);
330 ctx->naming_warnings++;
331 whine = ctx->naming_warnings == TOO_MANY_NAME_WARNINGS;
332 res = ctx->naming_warnings < TOO_MANY_NAME_WARNINGS;
333 pthread_mutex_unlock(&ctx->lock);
334
335 if (whine && !(debug || verbose))
336 str_info(ctx, ctx->mntpoint,
337_("More than %u naming warnings, shutting up."),
338 TOO_MANY_NAME_WARNINGS);
339
340 return debug || verbose || res;
341}