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