Line data Source code
1 : #include "clusterautoconfig.h"
2 :
3 : #include <unistd.h>
4 : #include <stdio.h>
5 : #include <stdint.h>
6 : #include <stdlib.h>
7 : #include <libgen.h>
8 : #include <string.h>
9 : #include <stdarg.h>
10 : #include <ctype.h>
11 : #include <signal.h>
12 : #include <libintl.h>
13 : #include <locale.h>
14 : #include <sys/time.h>
15 : #include <time.h>
16 : #define _(String) gettext(String)
17 : #include <syslog.h>
18 :
19 : #include <logging.h>
20 : #include "copyright.cf"
21 : #include "libgfs2.h"
22 : #include "fsck.h"
23 : #include "link.h"
24 : #include "osi_list.h"
25 : #include "metawalk.h"
26 : #include "util.h"
27 :
28 : struct lgfs2_inode *lf_dip = NULL; /* Lost and found directory inode */
29 : int lf_was_created = 0;
30 : uint64_t last_fs_block, last_reported_block = -1;
31 : int64_t last_reported_fblock = -1000000;
32 : int skip_this_pass = 0, fsck_abort = 0;
33 : int errors_found = 0, errors_corrected = 0;
34 : uint64_t last_data_block;
35 : uint64_t first_data_block;
36 : int dups_found = 0, dups_found_first = 0;
37 : int sb_fixed = 0;
38 : int print_level = MSG_NOTICE;
39 :
40 : static const char *pass_name = "";
41 :
42 0 : static void usage(char *name)
43 : {
44 0 : printf("Usage: %s [-afhnpqvVy] <device> \n", basename(name));
45 0 : }
46 :
47 0 : static void version(void)
48 : {
49 0 : printf( _("GFS2 fsck %s (built %s %s)\n"),
50 : VERSION, __DATE__, __TIME__);
51 0 : printf(REDHAT_COPYRIGHT "\n");
52 0 : }
53 :
54 64 : static int read_cmdline(int argc, char **argv, struct fsck_options *gopts)
55 : {
56 : int c;
57 :
58 127 : while ((c = getopt(argc, argv, "afhnpqvyV")) != -1) {
59 70 : switch(c) {
60 :
61 4 : case 'a':
62 : case 'p':
63 4 : if (gopts->yes || gopts->no) {
64 2 : fprintf(stderr, _("Options -p/-a, -y and -n may not be used together\n"));
65 2 : return FSCK_USAGE;
66 : }
67 2 : gopts->preen = 1;
68 2 : gopts->yes = 1;
69 2 : break;
70 0 : case 'f':
71 0 : gopts->force = 1;
72 0 : break;
73 0 : case 'h':
74 0 : usage(argv[0]);
75 0 : exit(FSCK_OK);
76 : break;
77 46 : case 'n':
78 46 : if (gopts->yes || gopts->preen) {
79 2 : fprintf(stderr, _("Options -p/-a, -y and -n may not be used together\n"));
80 2 : return FSCK_USAGE;
81 : }
82 44 : gopts->no = 1;
83 44 : break;
84 0 : case 'q':
85 0 : decrease_verbosity();
86 0 : break;
87 0 : case 'v':
88 0 : increase_verbosity();
89 0 : break;
90 0 : case 'V':
91 0 : version();
92 0 : exit(FSCK_OK);
93 : break;
94 19 : case 'y':
95 19 : if (gopts->no || gopts->preen) {
96 2 : fprintf(stderr, _("Options -p/-a, -y and -n may not be used together\n"));
97 2 : return FSCK_USAGE;
98 : }
99 17 : gopts->yes = 1;
100 17 : break;
101 1 : case ':':
102 : case '?':
103 1 : fprintf(stderr, _("Please use '-h' for help.\n"));
104 1 : return FSCK_USAGE;
105 0 : default:
106 0 : fprintf(stderr, _("Invalid option %c\n"), c);
107 0 : return FSCK_USAGE;
108 :
109 : }
110 : }
111 57 : if (argc > optind) {
112 57 : gopts->device = (argv[optind]);
113 57 : if (!gopts->device) {
114 0 : fprintf(stderr, _("Please use '-h' for help.\n"));
115 0 : return FSCK_USAGE;
116 : }
117 : } else {
118 0 : fprintf(stderr, _("No device specified (Please use '-h' for help)\n"));
119 0 : return FSCK_USAGE;
120 : }
121 57 : return 0;
122 : }
123 :
124 0 : static void interrupt(int sig)
125 : {
126 : char response;
127 : char progress[1024];
128 :
129 0 : if (!last_reported_block || last_reported_block == last_fs_block)
130 0 : snprintf(progress, sizeof(progress), _("progress unknown.\n"));
131 : else
132 0 : snprintf(progress, sizeof(progress),
133 0 : _("processing block %"PRIu64" out of %"PRIu64"\n"),
134 : last_reported_block, last_fs_block);
135 :
136 0 : response = generic_interrupt("fsck.gfs2", pass_name, progress,
137 0 : _("Do you want to abort fsck.gfs2, skip " \
138 : "the rest of this pass or continue " \
139 : "(a/s/c)?"), "asc");
140 0 : if (tolower(response) == 's') {
141 0 : skip_this_pass = 1;
142 0 : return;
143 : }
144 0 : else if (tolower(response) == 'a') {
145 0 : fsck_abort = 1;
146 0 : return;
147 : }
148 : }
149 :
150 56 : static int check_statfs(struct fsck_cx *cx)
151 : {
152 56 : struct osi_node *n, *next = NULL;
153 : struct lgfs2_rgrp_tree *rgd;
154 : struct gfs2_statfs_change sc;
155 56 : struct lgfs2_sbd *sdp = cx->sdp;
156 : uint64_t sc_total;
157 : uint64_t sc_free;
158 : uint64_t sc_dinodes;
159 : int count;
160 :
161 : /* Read the current statfs values */
162 56 : count = lgfs2_readi(sdp->md.statfs, &sc, 0, sdp->md.statfs->i_size);
163 56 : if (count != sizeof(struct gfs2_statfs_change)) {
164 0 : log_err(_("Failed to read statfs values (%d of %"PRIu64" read)\n"),
165 : count, sdp->md.statfs->i_size);
166 0 : return FSCK_ERROR;
167 : }
168 56 : sc_total = be64_to_cpu(sc.sc_total);
169 56 : sc_free = be64_to_cpu(sc.sc_free);
170 56 : sc_dinodes = be64_to_cpu(sc.sc_dinodes);
171 :
172 : /* Calculate the real values from the rgrp information */
173 56 : sdp->blks_total = 0;
174 56 : sdp->blks_alloced = 0;
175 56 : sdp->dinodes_alloced = 0;
176 :
177 4415 : for (n = osi_first(&sdp->rgtree); n; n = next) {
178 4359 : next = osi_next(n);
179 4359 : rgd = (struct lgfs2_rgrp_tree *)n;
180 4359 : sdp->blks_total += rgd->rt_data;
181 4359 : sdp->blks_alloced += (rgd->rt_data - rgd->rt_free);
182 4359 : sdp->dinodes_alloced += rgd->rt_dinodes;
183 : }
184 :
185 : /* See if they match */
186 56 : if (sc_total == sdp->blks_total &&
187 56 : sc_free == (sdp->blks_total - sdp->blks_alloced) &&
188 56 : sc_dinodes == sdp->dinodes_alloced) {
189 56 : log_info( _("The statfs file is accurate.\n"));
190 56 : return 0;
191 : }
192 0 : log_err( _("The statfs file is wrong:\n\n"));
193 0 : log_err( _("Current statfs values:\n"));
194 0 : log_err( _("blocks: %"PRId64" (0x%"PRIx64")\n"), sc_total, sc_total);
195 0 : log_err( _("free: %"PRId64" (0x%"PRIx64")\n"), sc_free, sc_free);
196 0 : log_err( _("dinodes: %"PRId64" (0x%"PRIx64")\n\n"), sc_dinodes, sc_dinodes);
197 0 : log_err( _("Calculated statfs values:\n"));
198 0 : log_err( _("blocks: %"PRIu64" (0x%"PRIx64")\n"),
199 : sdp->blks_total, sdp->blks_total);
200 0 : log_err( _("free: %"PRIu64" (0x%"PRIx64")\n"),
201 : (sdp->blks_total - sdp->blks_alloced),
202 : (sdp->blks_total - sdp->blks_alloced));
203 0 : log_err( _("dinodes: %"PRIu64" (0x%"PRIx64")\n"),
204 : sdp->dinodes_alloced, sdp->dinodes_alloced);
205 :
206 0 : errors_found++;
207 0 : if (!query(cx, _("Okay to fix the master statfs file? (y/n)"))) {
208 0 : log_err( _("The statfs file was not fixed.\n"));
209 0 : return 0;
210 : }
211 :
212 0 : lgfs2_init_statfs(sdp, NULL);
213 0 : log_err( _("The statfs file was fixed.\n"));
214 0 : errors_corrected++;
215 0 : return 0;
216 : }
217 :
218 : static const struct fsck_pass passes[] = {
219 : { .name = "pass1", .f = pass1 },
220 : { .name = "pass1b", .f = pass1b },
221 : { .name = "pass2", .f = pass2 },
222 : { .name = "pass3", .f = pass3 },
223 : { .name = "pass4", .f = pass4 },
224 : { .name = "check_statfs", .f = check_statfs },
225 : { .name = NULL, }
226 : };
227 :
228 336 : static int fsck_pass(const struct fsck_pass *p, struct fsck_cx *cx)
229 : {
230 : int ret;
231 : struct timeval timer;
232 :
233 336 : if (fsck_abort)
234 0 : return FSCK_CANCELED;
235 336 : pass_name = p->name;
236 :
237 336 : log_notice( _("Starting %s\n"), p->name);
238 336 : gettimeofday(&timer, NULL);
239 :
240 336 : ret = p->f(cx);
241 336 : if (ret)
242 0 : exit(ret);
243 336 : if (skip_this_pass || fsck_abort) {
244 0 : skip_this_pass = 0;
245 0 : log_notice( _("%s interrupted \n"), p->name);
246 0 : return FSCK_CANCELED;
247 : }
248 :
249 336 : print_pass_duration(p->name, &timer);
250 336 : return 0;
251 : }
252 :
253 : /*
254 : * on_exit() is non-standard but useful for reporting the exit status if it's
255 : * available.
256 : */
257 : #ifdef HAVE_ON_EXIT
258 64 : static void exitlog(int status, void *unused)
259 : {
260 64 : syslog(LOG_INFO, "exit: %d", status);
261 64 : }
262 : #else
263 : static void exitlog(void)
264 : {
265 : syslog(LOG_INFO, "exit.");
266 : }
267 : #endif
268 :
269 64 : static void startlog(int argc, char **argv)
270 : {
271 : int i;
272 : char *cmd, *p;
273 : size_t len;
274 :
275 197 : for (len = i = 0; i < argc; i++)
276 133 : len += strlen(argv[i]);
277 64 : len += argc; /* Add spaces and '\0' */
278 :
279 64 : cmd = malloc(len);
280 64 : if (cmd == NULL) {
281 0 : perror(argv[0]);
282 0 : exit(FSCK_ERROR);
283 : }
284 64 : p = cmd;
285 197 : for (i = 0; i < argc; i++, p++) {
286 133 : p = stpcpy(p, argv[i]);
287 133 : *p = ' ';
288 : }
289 64 : *(--p) = '\0';
290 64 : syslog(LOG_INFO, "started: %s", cmd);
291 64 : free(cmd);
292 64 : }
293 :
294 : #ifndef UNITTESTS
295 64 : int main(int argc, char **argv)
296 : {
297 64 : struct fsck_options opts = {0};
298 : struct lgfs2_sbd sb;
299 64 : struct fsck_cx cx = {
300 : .sdp = &sb,
301 : .opts = &opts,
302 : };
303 : int j;
304 : int i;
305 64 : int error = 0;
306 64 : int all_clean = 0;
307 64 : struct sigaction act = { .sa_handler = interrupt, };
308 :
309 64 : setlocale(LC_ALL, "");
310 64 : textdomain("gfs2-utils");
311 :
312 64 : openlog("fsck.gfs2", LOG_CONS|LOG_PID, LOG_USER);
313 64 : startlog(argc - 1, &argv[1]);
314 : #ifdef HAVE_ON_EXIT
315 64 : on_exit(exitlog, NULL);
316 : #else
317 : atexit(exitlog);
318 : #endif
319 :
320 64 : memset(&sb, 0, sizeof(sb));
321 :
322 64 : if ((error = read_cmdline(argc, argv, &opts)))
323 7 : exit(error);
324 57 : setbuf(stdout, NULL);
325 57 : log_notice( _("Initializing fsck\n"));
326 57 : if ((error = initialize(&cx, &all_clean)))
327 1 : exit(error);
328 :
329 56 : if (!opts.force && all_clean && opts.preen) {
330 0 : log_err( _("%s: clean.\n"), opts.device);
331 0 : destroy(&cx);
332 0 : exit(FSCK_OK);
333 : }
334 :
335 56 : sigaction(SIGINT, &act, NULL);
336 :
337 392 : for (i = 0; passes[i].name; i++)
338 336 : error = fsck_pass(passes + i, &cx);
339 :
340 : /* Free up our system inodes */
341 56 : lgfs2_inode_put(&sb.md.inum);
342 56 : lgfs2_inode_put(&sb.md.statfs);
343 119 : for (j = 0; j < sb.md.journals; j++)
344 63 : lgfs2_inode_put(&sb.md.journal[j]);
345 56 : free(sb.md.journal);
346 56 : sb.md.journal = NULL;
347 56 : lgfs2_inode_put(&sb.md.jiinode);
348 56 : lgfs2_inode_put(&sb.md.riinode);
349 56 : lgfs2_inode_put(&sb.md.qinode);
350 56 : lgfs2_inode_put(&sb.md.pinode);
351 56 : lgfs2_inode_put(&sb.md.rooti);
352 56 : lgfs2_inode_put(&sb.master_dir);
353 56 : if (lf_dip)
354 0 : lgfs2_inode_put(&lf_dip);
355 :
356 56 : if (!opts.no && errors_corrected)
357 14 : log_notice( _("Writing changes to disk\n"));
358 56 : fsync(sb.device_fd);
359 56 : link1_destroy(&nlink1map);
360 56 : link1_destroy(&clink1map);
361 56 : destroy(&cx);
362 56 : if (sb_fixed)
363 6 : log_warn(_("Superblock was reset. Use tunegfs2 to manually "
364 : "set lock table before mounting.\n"));
365 56 : log_notice( _("fsck.gfs2 complete\n"));
366 :
367 56 : if (!error) {
368 56 : if (!errors_found)
369 42 : error = FSCK_OK;
370 14 : else if (errors_found == errors_corrected)
371 14 : error = FSCK_NONDESTRUCT;
372 : else
373 0 : error = FSCK_UNCORRECTED;
374 : }
375 56 : exit(error);
376 : }
377 : #endif /* UNITTESTS */
|