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