Line data Source code
1 : #include "clusterautoconfig.h"
2 :
3 : #include <inttypes.h>
4 : #include <stdlib.h>
5 : #include <string.h>
6 : #include <unistd.h>
7 : #include <sys/time.h>
8 : #include <stdio.h>
9 : #include <stdarg.h>
10 : #include <termios.h>
11 : #include <libintl.h>
12 : #include <ctype.h>
13 : #define _(String) gettext(String)
14 :
15 : #include <logging.h>
16 : #include "libgfs2.h"
17 : #include "metawalk.h"
18 : #include "util.h"
19 :
20 : const char *reftypes[REF_TYPES + 1] = {"data", "metadata",
21 : "an extended attribute", "an inode",
22 : "unimportant"};
23 :
24 0 : void big_file_comfort(struct fsck_cx *cx, struct lgfs2_inode *ip, uint64_t blks_checked)
25 : {
26 : static struct timeval tv;
27 : static uint32_t seconds = 0;
28 : static uint64_t percent, fsize, chksize;
29 0 : uint64_t one_percent = 0;
30 : int i, cs;
31 0 : const char *human_abbrev = " KMGTPE";
32 :
33 0 : one_percent = ip->i_blocks / 100;
34 0 : if (blks_checked - last_reported_fblock < one_percent)
35 0 : return;
36 :
37 0 : last_reported_fblock = blks_checked;
38 0 : gettimeofday(&tv, NULL);
39 0 : if (!seconds)
40 0 : seconds = tv.tv_sec;
41 0 : if (tv.tv_sec == seconds)
42 0 : return;
43 :
44 0 : fsize = ip->i_size;
45 0 : for (i = 0; i < 6 && fsize > 1024; i++)
46 0 : fsize /= 1024;
47 0 : chksize = blks_checked * ip->i_sbd->sd_bsize;
48 0 : for (cs = 0; cs < 6 && chksize > 1024; cs++)
49 0 : chksize /= 1024;
50 0 : seconds = tv.tv_sec;
51 0 : percent = (blks_checked * 100) / ip->i_blocks;
52 0 : log_notice(_("\rChecking %"PRIu64"%c of %"PRIu64"%c of file at %"PRIu64" (0x%"PRIx64")"
53 : "- %"PRIu64" percent complete. \r"),
54 : chksize, human_abbrev[cs], fsize, human_abbrev[i], ip->i_num.in_addr,
55 : ip->i_num.in_addr, percent);
56 0 : fflush(stdout);
57 : }
58 :
59 477502169 : void display_progress(uint64_t block)
60 : {
61 : static uint64_t one_percent = 0;
62 : static struct timeval tv;
63 : static uint32_t seconds = 0;
64 :
65 477502169 : if (!one_percent)
66 56 : one_percent = last_fs_block / 100;
67 477502169 : if (!last_reported_block ||
68 477502169 : block - last_reported_block >= one_percent) {
69 5671 : last_reported_block = block;
70 5671 : gettimeofday(&tv, NULL);
71 5671 : if (!seconds)
72 56 : seconds = tv.tv_sec;
73 5671 : if (tv.tv_sec - seconds) {
74 : static uint64_t percent;
75 :
76 5 : seconds = tv.tv_sec;
77 5 : if (last_fs_block) {
78 5 : percent = (block * 100) / last_fs_block;
79 5 : log_notice(_("\r%"PRIu64" percent complete.\r"), percent);
80 5 : fflush(stdout);
81 : }
82 : }
83 : }
84 477502169 : }
85 :
86 0 : char fsck_getch(void)
87 : {
88 : struct termios termattr, savetermattr;
89 : char ch;
90 : ssize_t size;
91 :
92 0 : tcgetattr (STDIN_FILENO, &termattr);
93 0 : savetermattr = termattr;
94 0 : termattr.c_lflag &= ~(ICANON | IEXTEN | ISIG);
95 0 : termattr.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
96 0 : termattr.c_cflag &= ~(CSIZE | PARENB);
97 0 : termattr.c_cflag |= CS8;
98 0 : termattr.c_oflag &= ~(OPOST);
99 0 : termattr.c_cc[VMIN] = 0;
100 0 : termattr.c_cc[VTIME] = 0;
101 :
102 0 : tcsetattr (STDIN_FILENO, TCSANOW, &termattr);
103 : do {
104 0 : size = read(STDIN_FILENO, &ch, 1);
105 0 : if (size)
106 0 : break;
107 0 : usleep(50000);
108 0 : } while (!size);
109 :
110 0 : tcsetattr (STDIN_FILENO, TCSANOW, &savetermattr);
111 0 : return ch;
112 : }
113 :
114 0 : char generic_interrupt(const char *caller, const char *where,
115 : const char *progress, const char *question,
116 : const char *answers)
117 : {
118 : fd_set rfds;
119 : struct timeval tv;
120 : char response;
121 : int err, i;
122 :
123 0 : FD_ZERO(&rfds);
124 0 : FD_SET(STDIN_FILENO, &rfds);
125 :
126 0 : tv.tv_sec = 0;
127 0 : tv.tv_usec = 0;
128 : /* Make sure there isn't extraneous input before asking the
129 : * user the question */
130 0 : while((err = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv))) {
131 0 : if(err < 0) {
132 0 : log_debug("Error in select() on stdin\n");
133 0 : break;
134 : }
135 0 : if(read(STDIN_FILENO, &response, sizeof(char)) < 0) {
136 0 : log_debug("Error in read() on stdin\n");
137 0 : break;
138 : }
139 : }
140 : while (1) {
141 0 : printf("\n%s interrupted during %s: ", caller, where);
142 0 : if (progress)
143 0 : printf("%s.\n", progress);
144 0 : printf("%s", question);
145 :
146 : /* Make sure query is printed out */
147 0 : fflush(NULL);
148 0 : response = fsck_getch();
149 0 : printf("\n");
150 0 : fflush(NULL);
151 0 : if (strchr(answers, response))
152 0 : break;
153 0 : printf("Bad response, please type ");
154 0 : for (i = 0; i < strlen(answers) - 1; i++)
155 0 : printf("'%c', ", answers[i]);
156 0 : printf(" or '%c'.\n", answers[i]);
157 : }
158 0 : return response;
159 : }
160 :
161 : /* fsck_query: Same as gfs2_query except it adjusts errors_found and
162 : errors_corrected. */
163 32861 : int fsck_query(struct fsck_cx *cx, const char *format, ...)
164 : {
165 : va_list args;
166 : char response;
167 32861 : int ret = 0;
168 :
169 32861 : errors_found++;
170 32861 : fsck_abort = 0;
171 32861 : if (cx->opts->yes) {
172 32861 : errors_corrected++;
173 32861 : return 1;
174 : }
175 0 : if (cx->opts->no)
176 0 : return 0;
177 :
178 : while (1) {
179 0 : va_start(args, format);
180 0 : vprintf(format, args);
181 0 : va_end(args);
182 :
183 : /* Make sure query is printed out */
184 0 : fflush(NULL);
185 0 : response = fsck_getch();
186 :
187 0 : printf("\n");
188 0 : fflush(NULL);
189 0 : if (response == 0x3) { /* if interrupted, by ctrl-c */
190 0 : response = generic_interrupt("Question", "response",
191 : NULL,
192 : "Do you want to abort " \
193 : "or continue (a/c)?",
194 : "ac");
195 0 : if (response == 'a') {
196 0 : ret = 0;
197 0 : fsck_abort = 1;
198 0 : break;
199 : }
200 0 : printf("Continuing.\n");
201 0 : } else if (tolower(response) == 'y') {
202 0 : errors_corrected++;
203 0 : ret = 1;
204 0 : break;
205 0 : } else if (tolower(response) == 'n') {
206 0 : ret = 0;
207 0 : break;
208 : } else {
209 0 : printf("Bad response %d, please type 'y' or 'n'.\n",
210 : response);
211 : }
212 : }
213 0 : return ret;
214 : }
215 :
216 : /*
217 : * dup_set - Flag a block as a duplicate
218 : * We keep the references in a red/black tree. We can't keep track of every
219 : * single inode in the file system, so the first time this function is called
220 : * will actually be for the second reference to the duplicated block.
221 : * This will return the number of references to the block.
222 : *
223 : * create - will be set if the call is supposed to create the reference. */
224 0 : static struct duptree *dup_set(struct fsck_cx *cx, uint64_t dblock, int create)
225 : {
226 0 : struct osi_node **newn = &cx->dup_blocks.osi_node, *parent = NULL;
227 : struct duptree *dt;
228 :
229 : /* Figure out where to put new node */
230 0 : while (*newn) {
231 0 : struct duptree *cur = (struct duptree *)*newn;
232 :
233 0 : parent = *newn;
234 0 : if (dblock < cur->block)
235 0 : newn = &((*newn)->osi_left);
236 0 : else if (dblock > cur->block)
237 0 : newn = &((*newn)->osi_right);
238 : else
239 0 : return cur;
240 : }
241 :
242 0 : if (!create)
243 0 : return NULL;
244 0 : dt = malloc(sizeof(struct duptree));
245 0 : if (dt == NULL) {
246 0 : log_crit( _("Unable to allocate duptree structure\n"));
247 0 : return NULL;
248 : }
249 0 : dups_found++;
250 0 : memset(dt, 0, sizeof(struct duptree));
251 : /* Add new node and rebalance tree. */
252 0 : dt->block = dblock;
253 0 : dt->refs = 1; /* reference 1 is actually the reference we need to
254 : discover in pass1b. */
255 0 : osi_list_init(&dt->ref_inode_list);
256 0 : osi_list_init(&dt->ref_invinode_list);
257 0 : osi_link_node(&dt->node, parent, newn);
258 0 : osi_insert_color(&dt->node, &cx->dup_blocks);
259 :
260 0 : return dt;
261 : }
262 :
263 : /**
264 : * find_dup_ref_inode - find a duplicate reference inode entry for an inode
265 : */
266 0 : struct inode_with_dups *find_dup_ref_inode(struct duptree *dt,
267 : struct lgfs2_inode *ip)
268 : {
269 : osi_list_t *ref;
270 : struct inode_with_dups *id;
271 :
272 0 : osi_list_foreach(ref, &dt->ref_invinode_list) {
273 0 : id = osi_list_entry(ref, struct inode_with_dups, list);
274 :
275 0 : if (id->block_no == ip->i_num.in_addr)
276 0 : return id;
277 : }
278 0 : osi_list_foreach(ref, &dt->ref_inode_list) {
279 0 : id = osi_list_entry(ref, struct inode_with_dups, list);
280 :
281 0 : if (id->block_no == ip->i_num.in_addr)
282 0 : return id;
283 : }
284 0 : return NULL;
285 : }
286 :
287 : /**
288 : * count_dup_meta_refs - count the number of remaining references as metadata
289 : */
290 0 : int count_dup_meta_refs(struct duptree *dt)
291 : {
292 : osi_list_t *ref;
293 : struct inode_with_dups *id;
294 0 : int metarefs = 0;
295 :
296 0 : osi_list_foreach(ref, &dt->ref_invinode_list) {
297 0 : id = osi_list_entry(ref, struct inode_with_dups, list);
298 0 : if (id->reftypecount[REF_AS_META])
299 0 : metarefs++;
300 : }
301 0 : osi_list_foreach(ref, &dt->ref_inode_list) {
302 0 : id = osi_list_entry(ref, struct inode_with_dups, list);
303 0 : if (id->reftypecount[REF_AS_META])
304 0 : metarefs++;
305 : }
306 0 : return metarefs;
307 : }
308 :
309 : /*
310 : * add_duplicate_ref - Add a duplicate reference to the duplicates tree list
311 : * A new element of the tree will be created as needed
312 : * When the first reference is discovered in pass1, it realizes it's a
313 : * duplicate but it has already forgotten where the first reference was.
314 : * So we need to recreate the duplicate reference structure if it's not there.
315 : * Later, in pass1b, it has to go back through the file system
316 : * and figure out those original references in order to resolve them.
317 : *
318 : * first - if 1, we're being called from pass1b, in which case we're trying
319 : * to find the first reference to this block. If 0, we're being
320 : * called from pass1, which is the second reference, which determined
321 : * it was a duplicate..
322 : */
323 0 : int add_duplicate_ref(struct fsck_cx *cx, struct lgfs2_inode *ip, uint64_t block,
324 : enum dup_ref_type reftype, int first, int inode_valid)
325 : {
326 : struct inode_with_dups *id;
327 : struct duptree *dt;
328 :
329 0 : if (!valid_block_ip(ip, block))
330 0 : return META_IS_GOOD;
331 : /* If this is not the first reference (i.e. all calls from pass1) we
332 : need to create the duplicate reference. If this is pass1b, we want
333 : to ignore references that aren't found. */
334 0 : dt = dup_set(cx, block, !first);
335 0 : if (!dt) /* If this isn't a duplicate */
336 0 : return META_IS_GOOD;
337 :
338 : /* If we found the duplicate reference but we've already discovered
339 : the first reference (in pass1b) and the other references in pass1,
340 : we don't need to count it, so just return. */
341 0 : if (dt->dup_flags & DUPFLAG_REF1_FOUND)
342 0 : return META_IS_GOOD;
343 :
344 : /* Check for a previous reference to this duplicate */
345 0 : id = find_dup_ref_inode(dt, ip);
346 :
347 : /* We have to be careful here. The original referencing dinode may have
348 : deemed to be bad and deleted/freed in pass1. In that case, pass1b
349 : wouldn't discover the correct [deleted] original reference. In
350 : that case, we don't want to be confused and consider this second
351 : reference the same as the first. If we do, we'll never be able to
352 : resolve it. The first reference can't be the second reference. */
353 0 : if (id && first && !(dt->dup_flags & DUPFLAG_REF1_FOUND)) {
354 0 : log_info(_("Original reference to block %"PRIu64" (0x%"PRIx64") was "
355 : "either found to be bad and deleted, or else "
356 : "a duplicate within the same inode.\n"),
357 : block, block);
358 0 : log_info(_("I'll consider the reference from inode %"PRIu64
359 : " (0x%"PRIx64") the first reference.\n"),
360 : ip->i_num.in_addr, ip->i_num.in_addr);
361 0 : dt->dup_flags |= DUPFLAG_REF1_IS_DUPL;
362 0 : dt->refs++;
363 : }
364 :
365 : /* The first time this is called from pass1 is actually the second
366 : reference. When we go back in pass1b looking for the original
367 : reference, we don't want to increment the reference count because
368 : it's already accounted for. */
369 0 : if (first) {
370 0 : dt->dup_flags |= DUPFLAG_REF1_FOUND;
371 0 : dups_found_first++; /* We found another first ref. */
372 : } else {
373 0 : dt->refs++;
374 : }
375 :
376 0 : if (id == NULL) {
377 : /* Check for the inode on the invalid inode reference list. */
378 : int q;
379 :
380 0 : id = calloc(1, sizeof(*id));
381 0 : if (!id) {
382 0 : log_crit( _("Unable to allocate inode_with_dups structure\n"));
383 0 : return META_ERROR;
384 : }
385 0 : id->block_no = ip->i_num.in_addr;
386 0 : q = bitmap_type(ip->i_sbd, ip->i_num.in_addr);
387 : /* If it's an invalid dinode, put it first on the invalid
388 : inode reference list otherwise put it on the normal list. */
389 0 : if (!inode_valid || q == GFS2_BLKST_UNLINKED)
390 0 : osi_list_add_prev(&id->list, &dt->ref_invinode_list);
391 : else {
392 : /* If this is a system dinode, we want the duplicate
393 : processing to find it first. That way references
394 : from inside journals, et al, will take priority.
395 : We don't want to delete journals in favor of dinodes
396 : that reference a block inside a journal. */
397 0 : if (fsck_system_inode(ip->i_sbd, id->block_no))
398 0 : osi_list_add(&id->list, &dt->ref_inode_list);
399 : else
400 0 : osi_list_add_prev(&id->list,
401 : &dt->ref_inode_list);
402 : }
403 : }
404 0 : id->reftypecount[reftype]++;
405 0 : id->dup_count++;
406 0 : log_info(_("Found %d reference(s) to block %"PRIu64" (0x%"PRIx64") "
407 : "as %s in %s inode #%"PRIu64" (0x%"PRIx64")\n"),
408 : id->dup_count, block, block, reftypes[reftype],
409 : inode_valid ? _("valid") : _("invalid"),
410 : ip->i_num.in_addr, ip->i_num.in_addr);
411 0 : if (first)
412 0 : log_info( _("This is the original reference.\n"));
413 : else {
414 : /* Check for duplicate refs to the same block in one inode. */
415 0 : if (id->dup_count > 1)
416 0 : dt->dup_flags |= DUPFLAG_REF1_FOUND;
417 0 : log_info( _("This brings the total to: %d inode references, "
418 : "%d from this inode.\n"),
419 : dt->refs, id->dup_count);
420 : }
421 0 : return META_IS_GOOD;
422 : }
423 :
424 224 : struct dir_info *dirtree_insert(struct fsck_cx *cx, struct lgfs2_inum inum)
425 : {
426 224 : struct osi_node **newn = &cx->dirtree.osi_node, *parent = NULL;
427 : struct dir_info *data;
428 :
429 : /* Figure out where to put new node */
430 504 : while (*newn) {
431 280 : struct dir_info *cur = (struct dir_info *)*newn;
432 :
433 280 : parent = *newn;
434 280 : if (inum.in_addr < cur->dinode.in_addr)
435 112 : newn = &((*newn)->osi_left);
436 168 : else if (inum.in_addr > cur->dinode.in_addr)
437 168 : newn = &((*newn)->osi_right);
438 : else
439 0 : return cur;
440 : }
441 :
442 224 : data = calloc(1, sizeof(struct dir_info));
443 224 : if (!data) {
444 0 : log_crit( _("Unable to allocate dir_info structure\n"));
445 0 : return NULL;
446 : }
447 : /* Add new node and rebalance tree. */
448 224 : data->dinode.in_addr = inum.in_addr;
449 224 : data->dinode.in_formal_ino = inum.in_formal_ino;
450 224 : osi_link_node(&data->node, parent, newn);
451 224 : osi_insert_color(&data->node, &cx->dirtree);
452 :
453 224 : return data;
454 : }
455 :
456 3894 : struct dir_info *dirtree_find(struct fsck_cx *cx, uint64_t block)
457 : {
458 3894 : struct osi_node *node = cx->dirtree.osi_node;
459 :
460 10417 : while (node) {
461 8987 : struct dir_info *data = (struct dir_info *)node;
462 :
463 8987 : if (block < data->dinode.in_addr)
464 2998 : node = node->osi_left;
465 5989 : else if (block > data->dinode.in_addr)
466 3525 : node = node->osi_right;
467 : else
468 2464 : return data;
469 : }
470 1430 : return NULL;
471 : }
472 :
473 : /* get_ref_type - figure out if all duplicate references from this inode
474 : are the same type, and if so, return the type. */
475 0 : enum dup_ref_type get_ref_type(struct inode_with_dups *id)
476 : {
477 : enum dup_ref_type t, i;
478 : int found_type_with_ref;
479 : int found_other_types;
480 :
481 0 : for (t = REF_AS_DATA; t < REF_TYPES; t++) {
482 0 : found_type_with_ref = 0;
483 0 : found_other_types = 0;
484 0 : for (i = REF_AS_DATA; i < REF_TYPES; i++) {
485 0 : if (id->reftypecount[i]) {
486 0 : if (t == i)
487 0 : found_type_with_ref = 1;
488 : else
489 0 : found_other_types = 1;
490 : }
491 : }
492 0 : if (found_type_with_ref)
493 0 : return found_other_types ? REF_TYPES : t;
494 : }
495 0 : return REF_TYPES;
496 : }
497 :
498 0 : void dup_listent_delete(struct duptree *dt, struct inode_with_dups *id)
499 : {
500 0 : log_err(_("Removing duplicate reference to block %"PRIu64" (0x%"PRIx64") "
501 : "referenced as %s by dinode %"PRIu64" (0x%"PRIx64")\n"),
502 : dt->block, dt->block, reftypes[get_ref_type(id)], id->block_no, id->block_no);
503 0 : dt->refs--;
504 0 : if (id->name)
505 0 : free(id->name);
506 0 : osi_list_del(&id->list);
507 0 : free(id);
508 0 : }
509 :
510 0 : void dup_delete(struct fsck_cx *cx, struct duptree *dt)
511 : {
512 : struct inode_with_dups *id;
513 : osi_list_t *tmp;
514 :
515 0 : while (!osi_list_empty(&dt->ref_invinode_list)) {
516 0 : tmp = (&dt->ref_invinode_list)->next;
517 0 : id = osi_list_entry(tmp, struct inode_with_dups, list);
518 0 : dup_listent_delete(dt, id);
519 : }
520 0 : while (!osi_list_empty(&dt->ref_inode_list)) {
521 0 : tmp = (&dt->ref_inode_list)->next;
522 0 : id = osi_list_entry(tmp, struct inode_with_dups, list);
523 0 : dup_listent_delete(dt, id);
524 : }
525 0 : osi_erase(&dt->node, &cx->dup_blocks);
526 0 : free(dt);
527 0 : }
528 :
529 224 : void dirtree_delete(struct fsck_cx *cx, struct dir_info *b)
530 : {
531 224 : osi_erase(&b->node, &cx->dirtree);
532 224 : free(b);
533 224 : }
534 :
535 0 : uint64_t find_free_blk(struct lgfs2_sbd *sdp)
536 : {
537 0 : struct osi_node *n, *next = NULL;
538 0 : struct lgfs2_rgrp_tree *rl = NULL;
539 : struct gfs2_rgrp *rg;
540 0 : unsigned int block, bn = 0, x = 0, y = 0;
541 : unsigned int state;
542 :
543 0 : memset(&rg, 0, sizeof(rg));
544 0 : for (n = osi_first(&sdp->rgtree); n; n = next) {
545 0 : next = osi_next(n);
546 0 : rl = (struct lgfs2_rgrp_tree *)n;
547 0 : if (rl->rt_free)
548 0 : break;
549 : }
550 :
551 0 : if (n == NULL)
552 0 : return 0;
553 :
554 0 : for (block = 0; block < rl->rt_length; block++) {
555 0 : char *buf = rl->rt_bits[block].bi_data;
556 0 : x = (block) ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_rgrp);
557 :
558 0 : for (; x < sdp->sd_bsize; x++)
559 0 : for (y = 0; y < GFS2_NBBY; y++) {
560 0 : state = (buf[x] >> (GFS2_BIT_SIZE * y)) & 0x03;
561 0 : if (state == GFS2_BLKST_FREE)
562 0 : return rl->rt_data0 + bn;
563 0 : bn++;
564 : }
565 : }
566 0 : return 0;
567 : }
568 :
569 29 : __be64 *get_dir_hash(struct lgfs2_inode *ip)
570 : {
571 29 : unsigned hsize = (1 << ip->i_depth) * sizeof(uint64_t);
572 : int ret;
573 29 : __be64 *tbl = malloc(hsize);
574 :
575 29 : if (tbl == NULL)
576 0 : return NULL;
577 :
578 29 : ret = lgfs2_readi(ip, tbl, 0, hsize);
579 29 : if (ret != hsize) {
580 0 : free(tbl);
581 0 : return NULL;
582 : }
583 :
584 29 : return tbl;
585 : }
586 :
587 1 : void delete_all_dups(struct fsck_cx *cx, struct lgfs2_inode *ip)
588 : {
589 : struct osi_node *n, *next;
590 : struct duptree *dt;
591 : osi_list_t *tmp, *x;
592 : struct inode_with_dups *id;
593 : int found;
594 :
595 1 : for (n = osi_first(&cx->dup_blocks); n; n = next) {
596 0 : next = osi_next(n);
597 0 : dt = (struct duptree *)n;
598 :
599 0 : found = 0;
600 0 : id = NULL;
601 :
602 0 : osi_list_foreach_safe(tmp, &dt->ref_invinode_list, x) {
603 0 : id = osi_list_entry(tmp, struct inode_with_dups, list);
604 0 : if (id->block_no == ip->i_num.in_addr) {
605 0 : dup_listent_delete(dt, id);
606 0 : found = 1;
607 : }
608 : }
609 0 : osi_list_foreach_safe(tmp, &dt->ref_inode_list, x) {
610 0 : id = osi_list_entry(tmp, struct inode_with_dups, list);
611 0 : if (id->block_no == ip->i_num.in_addr) {
612 0 : dup_listent_delete(dt, id);
613 0 : found = 1;
614 : }
615 : }
616 0 : if (!found)
617 0 : continue;
618 :
619 0 : if (dt->refs == 0) {
620 0 : log_debug(_("This was the last reference: 0x%"PRIx64" is "
621 : "no longer a duplicate.\n"),
622 : dt->block);
623 0 : dup_delete(cx, dt);
624 : } else {
625 0 : log_debug(_("%d references remain to 0x%"PRIx64"\n"),
626 : dt->refs, dt->block);
627 0 : if (dt->refs > 1)
628 0 : continue;
629 :
630 0 : id = NULL;
631 0 : osi_list_foreach(tmp, &dt->ref_invinode_list)
632 0 : id = osi_list_entry(tmp,
633 : struct inode_with_dups,
634 : list);
635 0 : osi_list_foreach(tmp, &dt->ref_inode_list)
636 0 : id = osi_list_entry(tmp,
637 : struct inode_with_dups,
638 : list);
639 0 : if (id)
640 0 : log_debug("Last reference is from inode 0x%"PRIx64"\n",
641 : id->block_no);
642 : }
643 : }
644 1 : }
645 :
646 392 : void print_pass_duration(const char *name, struct timeval *start)
647 : {
648 392 : char duration[17] = ""; /* strlen("XXdXXhXXmXX.XXXs") + 1 */
649 : struct timeval end, diff;
650 : unsigned d, h, m, s;
651 392 : char *p = duration;
652 :
653 392 : gettimeofday(&end, NULL);
654 392 : timersub(&end, start, &diff);
655 :
656 392 : s = diff.tv_sec % 60;
657 392 : diff.tv_sec /= 60;
658 392 : m = diff.tv_sec % 60;
659 392 : diff.tv_sec /= 60;
660 392 : h = diff.tv_sec % 24;
661 392 : d = diff.tv_sec / 24;
662 :
663 392 : if (d)
664 0 : p += snprintf(p, 4, "%ud", d > 99 ? 99U : d);
665 392 : if (h)
666 0 : p += snprintf(p, 4, "%uh", h);
667 392 : if (m)
668 0 : p += snprintf(p, 4, "%um", m);
669 :
670 392 : snprintf(p, 8, "%u.%03lus", s, diff.tv_usec / 1000);
671 392 : log_notice(_("%s completed in %s\n"), name, duration);
672 392 : }
673 :
|