|
Packit |
6ef888 |
#include "clusterautoconfig.h"
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
#include <inttypes.h>
|
|
Packit |
6ef888 |
#include <stdlib.h>
|
|
Packit |
6ef888 |
#include <string.h>
|
|
Packit |
6ef888 |
#include <unistd.h>
|
|
Packit |
6ef888 |
#include <sys/time.h>
|
|
Packit |
6ef888 |
#include <stdio.h>
|
|
Packit |
6ef888 |
#include <stdarg.h>
|
|
Packit |
6ef888 |
#include <termios.h>
|
|
Packit |
6ef888 |
#include <libintl.h>
|
|
Packit |
6ef888 |
#include <ctype.h>
|
|
Packit |
6ef888 |
#define _(String) gettext(String)
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
#include <logging.h>
|
|
Packit |
6ef888 |
#include "libgfs2.h"
|
|
Packit |
6ef888 |
#include "metawalk.h"
|
|
Packit |
6ef888 |
#include "util.h"
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
const char *reftypes[ref_types + 1] = {"data", "metadata",
|
|
Packit |
6ef888 |
"an extended attribute", "an inode",
|
|
Packit |
6ef888 |
"unimportant"};
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
void big_file_comfort(struct gfs2_inode *ip, uint64_t blks_checked)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
static struct timeval tv;
|
|
Packit |
6ef888 |
static uint32_t seconds = 0;
|
|
Packit |
6ef888 |
static uint64_t percent, fsize, chksize;
|
|
Packit |
6ef888 |
uint64_t one_percent = 0;
|
|
Packit |
6ef888 |
int i, cs;
|
|
Packit |
6ef888 |
const char *human_abbrev = " KMGTPE";
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
one_percent = ip->i_di.di_blocks / 100;
|
|
Packit |
6ef888 |
if (blks_checked - last_reported_fblock < one_percent)
|
|
Packit |
6ef888 |
return;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
last_reported_fblock = blks_checked;
|
|
Packit |
6ef888 |
gettimeofday(&tv, NULL);
|
|
Packit |
6ef888 |
if (!seconds)
|
|
Packit |
6ef888 |
seconds = tv.tv_sec;
|
|
Packit |
6ef888 |
if (tv.tv_sec == seconds)
|
|
Packit |
6ef888 |
return;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
fsize = ip->i_di.di_size;
|
|
Packit |
6ef888 |
for (i = 0; i < 6 && fsize > 1024; i++)
|
|
Packit |
6ef888 |
fsize /= 1024;
|
|
Packit |
6ef888 |
chksize = blks_checked * ip->i_sbd->bsize;
|
|
Packit |
6ef888 |
for (cs = 0; cs < 6 && chksize > 1024; cs++)
|
|
Packit |
6ef888 |
chksize /= 1024;
|
|
Packit |
6ef888 |
seconds = tv.tv_sec;
|
|
Packit |
6ef888 |
percent = (blks_checked * 100) / ip->i_di.di_blocks;
|
|
Packit |
6ef888 |
log_notice( _("\rChecking %lld%c of %lld%c of file at %lld (0x%llx)"
|
|
Packit |
6ef888 |
"- %llu percent complete. \r"),
|
|
Packit |
6ef888 |
(long long)chksize, human_abbrev[cs],
|
|
Packit |
6ef888 |
(unsigned long long)fsize, human_abbrev[i],
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)percent);
|
|
Packit |
6ef888 |
fflush(stdout);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* Put out a warm, fuzzy message every second so the user */
|
|
Packit |
6ef888 |
/* doesn't think we hung. (This may take a long time). */
|
|
Packit |
6ef888 |
void warm_fuzzy_stuff(uint64_t block)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
static uint64_t one_percent = 0;
|
|
Packit |
6ef888 |
static struct timeval tv;
|
|
Packit |
6ef888 |
static uint32_t seconds = 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (!one_percent)
|
|
Packit |
6ef888 |
one_percent = last_fs_block / 100;
|
|
Packit |
6ef888 |
if (!last_reported_block ||
|
|
Packit |
6ef888 |
block - last_reported_block >= one_percent) {
|
|
Packit |
6ef888 |
last_reported_block = block;
|
|
Packit |
6ef888 |
gettimeofday(&tv, NULL);
|
|
Packit |
6ef888 |
if (!seconds)
|
|
Packit |
6ef888 |
seconds = tv.tv_sec;
|
|
Packit |
6ef888 |
if (tv.tv_sec - seconds) {
|
|
Packit |
6ef888 |
static uint64_t percent;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
seconds = tv.tv_sec;
|
|
Packit |
6ef888 |
if (last_fs_block) {
|
|
Packit |
6ef888 |
percent = (block * 100) / last_fs_block;
|
|
Packit |
6ef888 |
log_notice( _("\r%llu percent complete.\r"),
|
|
Packit |
6ef888 |
(unsigned long long)percent);
|
|
Packit |
6ef888 |
fflush(stdout);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
char gfs2_getch(void)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct termios termattr, savetermattr;
|
|
Packit |
6ef888 |
char ch;
|
|
Packit |
6ef888 |
ssize_t size;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
tcgetattr (STDIN_FILENO, &termattr);
|
|
Packit |
6ef888 |
savetermattr = termattr;
|
|
Packit |
6ef888 |
termattr.c_lflag &= ~(ICANON | IEXTEN | ISIG);
|
|
Packit |
6ef888 |
termattr.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
|
Packit |
6ef888 |
termattr.c_cflag &= ~(CSIZE | PARENB);
|
|
Packit |
6ef888 |
termattr.c_cflag |= CS8;
|
|
Packit |
6ef888 |
termattr.c_oflag &= ~(OPOST);
|
|
Packit |
6ef888 |
termattr.c_cc[VMIN] = 0;
|
|
Packit |
6ef888 |
termattr.c_cc[VTIME] = 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
tcsetattr (STDIN_FILENO, TCSANOW, &termattr);
|
|
Packit |
6ef888 |
do {
|
|
Packit |
6ef888 |
size = read(STDIN_FILENO, &ch, 1);
|
|
Packit |
6ef888 |
if (size)
|
|
Packit |
6ef888 |
break;
|
|
Packit |
6ef888 |
usleep(50000);
|
|
Packit |
6ef888 |
} while (!size);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
tcsetattr (STDIN_FILENO, TCSANOW, &savetermattr);
|
|
Packit |
6ef888 |
return ch;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
char generic_interrupt(const char *caller, const char *where,
|
|
Packit |
6ef888 |
const char *progress, const char *question,
|
|
Packit |
6ef888 |
const char *answers)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
fd_set rfds;
|
|
Packit |
6ef888 |
struct timeval tv;
|
|
Packit |
6ef888 |
char response;
|
|
Packit |
6ef888 |
int err, i;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
FD_ZERO(&rfds);
|
|
Packit |
6ef888 |
FD_SET(STDIN_FILENO, &rfds);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
tv.tv_sec = 0;
|
|
Packit |
6ef888 |
tv.tv_usec = 0;
|
|
Packit |
6ef888 |
/* Make sure there isn't extraneous input before asking the
|
|
Packit |
6ef888 |
* user the question */
|
|
Packit |
6ef888 |
while((err = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv))) {
|
|
Packit |
6ef888 |
if(err < 0) {
|
|
Packit |
6ef888 |
log_debug("Error in select() on stdin\n");
|
|
Packit |
6ef888 |
break;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
if(read(STDIN_FILENO, &response, sizeof(char)) < 0) {
|
|
Packit |
6ef888 |
log_debug("Error in read() on stdin\n");
|
|
Packit |
6ef888 |
break;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
while (TRUE) {
|
|
Packit |
6ef888 |
printf("\n%s interrupted during %s: ", caller, where);
|
|
Packit |
6ef888 |
if (progress)
|
|
Packit |
6ef888 |
printf("%s.\n", progress);
|
|
Packit |
6ef888 |
printf("%s", question);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* Make sure query is printed out */
|
|
Packit |
6ef888 |
fflush(NULL);
|
|
Packit |
6ef888 |
response = gfs2_getch();
|
|
Packit |
6ef888 |
printf("\n");
|
|
Packit |
6ef888 |
fflush(NULL);
|
|
Packit |
6ef888 |
if (strchr(answers, response))
|
|
Packit |
6ef888 |
break;
|
|
Packit |
6ef888 |
printf("Bad response, please type ");
|
|
Packit |
6ef888 |
for (i = 0; i < strlen(answers) - 1; i++)
|
|
Packit |
6ef888 |
printf("'%c', ", answers[i]);
|
|
Packit |
6ef888 |
printf(" or '%c'.\n", answers[i]);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return response;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* fsck_query: Same as gfs2_query except it adjusts errors_found and
|
|
Packit |
6ef888 |
errors_corrected. */
|
|
Packit |
6ef888 |
int fsck_query(const char *format, ...)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
va_list args;
|
|
Packit |
6ef888 |
char response;
|
|
Packit |
6ef888 |
int ret = 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
errors_found++;
|
|
Packit |
6ef888 |
fsck_abort = 0;
|
|
Packit |
6ef888 |
if (opts.yes) {
|
|
Packit |
6ef888 |
errors_corrected++;
|
|
Packit |
6ef888 |
return 1;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
if (opts.no)
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
opts.query = TRUE;
|
|
Packit |
6ef888 |
while (1) {
|
|
Packit |
6ef888 |
va_start(args, format);
|
|
Packit |
6ef888 |
vprintf(format, args);
|
|
Packit |
6ef888 |
va_end(args);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* Make sure query is printed out */
|
|
Packit |
6ef888 |
fflush(NULL);
|
|
Packit |
6ef888 |
response = gfs2_getch();
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
printf("\n");
|
|
Packit |
6ef888 |
fflush(NULL);
|
|
Packit |
6ef888 |
if (response == 0x3) { /* if interrupted, by ctrl-c */
|
|
Packit |
6ef888 |
response = generic_interrupt("Question", "response",
|
|
Packit |
6ef888 |
NULL,
|
|
Packit |
6ef888 |
"Do you want to abort " \
|
|
Packit |
6ef888 |
"or continue (a/c)?",
|
|
Packit |
6ef888 |
"ac");
|
|
Packit |
6ef888 |
if (response == 'a') {
|
|
Packit |
6ef888 |
ret = 0;
|
|
Packit |
6ef888 |
fsck_abort = 1;
|
|
Packit |
6ef888 |
break;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
printf("Continuing.\n");
|
|
Packit |
6ef888 |
} else if (tolower(response) == 'y') {
|
|
Packit |
6ef888 |
errors_corrected++;
|
|
Packit |
6ef888 |
ret = 1;
|
|
Packit |
6ef888 |
break;
|
|
Packit |
6ef888 |
} else if (tolower(response) == 'n') {
|
|
Packit |
6ef888 |
ret = 0;
|
|
Packit |
6ef888 |
break;
|
|
Packit |
6ef888 |
} else {
|
|
Packit |
6ef888 |
printf("Bad response %d, please type 'y' or 'n'.\n",
|
|
Packit |
6ef888 |
response);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
opts.query = FALSE;
|
|
Packit |
6ef888 |
return ret;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/*
|
|
Packit |
6ef888 |
* gfs2_dup_set - Flag a block as a duplicate
|
|
Packit |
6ef888 |
* We keep the references in a red/black tree. We can't keep track of every
|
|
Packit |
6ef888 |
* single inode in the file system, so the first time this function is called
|
|
Packit |
6ef888 |
* will actually be for the second reference to the duplicated block.
|
|
Packit |
6ef888 |
* This will return the number of references to the block.
|
|
Packit |
6ef888 |
*
|
|
Packit |
6ef888 |
* create - will be set if the call is supposed to create the reference. */
|
|
Packit |
6ef888 |
static struct duptree *gfs2_dup_set(uint64_t dblock, int create)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct osi_node **newn = &dup_blocks.osi_node, *parent = NULL;
|
|
Packit |
6ef888 |
struct duptree *dt;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* Figure out where to put new node */
|
|
Packit |
6ef888 |
while (*newn) {
|
|
Packit |
6ef888 |
struct duptree *cur = (struct duptree *)*newn;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
parent = *newn;
|
|
Packit |
6ef888 |
if (dblock < cur->block)
|
|
Packit |
6ef888 |
newn = &((*newn)->osi_left);
|
|
Packit |
6ef888 |
else if (dblock > cur->block)
|
|
Packit |
6ef888 |
newn = &((*newn)->osi_right);
|
|
Packit |
6ef888 |
else
|
|
Packit |
6ef888 |
return cur;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (!create)
|
|
Packit |
6ef888 |
return NULL;
|
|
Packit |
6ef888 |
dt = malloc(sizeof(struct duptree));
|
|
Packit |
6ef888 |
if (dt == NULL) {
|
|
Packit |
6ef888 |
log_crit( _("Unable to allocate duptree structure\n"));
|
|
Packit |
6ef888 |
return NULL;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
dups_found++;
|
|
Packit |
6ef888 |
memset(dt, 0, sizeof(struct duptree));
|
|
Packit |
6ef888 |
/* Add new node and rebalance tree. */
|
|
Packit |
6ef888 |
dt->block = dblock;
|
|
Packit |
6ef888 |
dt->refs = 1; /* reference 1 is actually the reference we need to
|
|
Packit |
6ef888 |
discover in pass1b. */
|
|
Packit |
6ef888 |
osi_list_init(&dt->ref_inode_list);
|
|
Packit |
6ef888 |
osi_list_init(&dt->ref_invinode_list);
|
|
Packit |
6ef888 |
osi_link_node(&dt->node, parent, newn);
|
|
Packit |
6ef888 |
osi_insert_color(&dt->node, &dup_blocks);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
return dt;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/**
|
|
Packit |
6ef888 |
* find_dup_ref_inode - find a duplicate reference inode entry for an inode
|
|
Packit |
6ef888 |
*/
|
|
Packit |
6ef888 |
struct inode_with_dups *find_dup_ref_inode(struct duptree *dt,
|
|
Packit |
6ef888 |
struct gfs2_inode *ip)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
osi_list_t *ref;
|
|
Packit |
6ef888 |
struct inode_with_dups *id;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
osi_list_foreach(ref, &dt->ref_invinode_list) {
|
|
Packit |
6ef888 |
id = osi_list_entry(ref, struct inode_with_dups, list);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (id->block_no == ip->i_di.di_num.no_addr)
|
|
Packit |
6ef888 |
return id;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
osi_list_foreach(ref, &dt->ref_inode_list) {
|
|
Packit |
6ef888 |
id = osi_list_entry(ref, struct inode_with_dups, list);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (id->block_no == ip->i_di.di_num.no_addr)
|
|
Packit |
6ef888 |
return id;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return NULL;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/**
|
|
Packit |
6ef888 |
* count_dup_meta_refs - count the number of remaining references as metadata
|
|
Packit |
6ef888 |
*/
|
|
Packit |
6ef888 |
int count_dup_meta_refs(struct duptree *dt)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
osi_list_t *ref;
|
|
Packit |
6ef888 |
struct inode_with_dups *id;
|
|
Packit |
6ef888 |
int metarefs = 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
osi_list_foreach(ref, &dt->ref_invinode_list) {
|
|
Packit |
6ef888 |
id = osi_list_entry(ref, struct inode_with_dups, list);
|
|
Packit |
6ef888 |
if (id->reftypecount[ref_as_meta])
|
|
Packit |
6ef888 |
metarefs++;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
osi_list_foreach(ref, &dt->ref_inode_list) {
|
|
Packit |
6ef888 |
id = osi_list_entry(ref, struct inode_with_dups, list);
|
|
Packit |
6ef888 |
if (id->reftypecount[ref_as_meta])
|
|
Packit |
6ef888 |
metarefs++;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return metarefs;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/*
|
|
Packit |
6ef888 |
* add_duplicate_ref - Add a duplicate reference to the duplicates tree list
|
|
Packit |
6ef888 |
* A new element of the tree will be created as needed
|
|
Packit |
6ef888 |
* When the first reference is discovered in pass1, it realizes it's a
|
|
Packit |
6ef888 |
* duplicate but it has already forgotten where the first reference was.
|
|
Packit |
6ef888 |
* So we need to recreate the duplicate reference structure if it's not there.
|
|
Packit |
6ef888 |
* Later, in pass1b, it has to go back through the file system
|
|
Packit |
6ef888 |
* and figure out those original references in order to resolve them.
|
|
Packit |
6ef888 |
*
|
|
Packit |
6ef888 |
* first - if 1, we're being called from pass1b, in which case we're trying
|
|
Packit |
6ef888 |
* to find the first reference to this block. If 0, we're being
|
|
Packit |
6ef888 |
* called from pass1, which is the second reference, which determined
|
|
Packit |
6ef888 |
* it was a duplicate..
|
|
Packit |
6ef888 |
*/
|
|
Packit |
6ef888 |
int add_duplicate_ref(struct gfs2_inode *ip, uint64_t block,
|
|
Packit |
6ef888 |
enum dup_ref_type reftype, int first, int inode_valid)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct inode_with_dups *id;
|
|
Packit |
6ef888 |
struct duptree *dt;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (!valid_block_ip(ip, block))
|
|
Packit |
6ef888 |
return meta_is_good;
|
|
Packit |
6ef888 |
/* If this is not the first reference (i.e. all calls from pass1) we
|
|
Packit |
6ef888 |
need to create the duplicate reference. If this is pass1b, we want
|
|
Packit |
6ef888 |
to ignore references that aren't found. */
|
|
Packit |
6ef888 |
dt = gfs2_dup_set(block, !first);
|
|
Packit |
6ef888 |
if (!dt) /* If this isn't a duplicate */
|
|
Packit |
6ef888 |
return meta_is_good;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* If we found the duplicate reference but we've already discovered
|
|
Packit |
6ef888 |
the first reference (in pass1b) and the other references in pass1,
|
|
Packit |
6ef888 |
we don't need to count it, so just return. */
|
|
Packit |
6ef888 |
if (dt->dup_flags & DUPFLAG_REF1_FOUND)
|
|
Packit |
6ef888 |
return meta_is_good;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* Check for a previous reference to this duplicate */
|
|
Packit |
6ef888 |
id = find_dup_ref_inode(dt, ip);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* We have to be careful here. The original referencing dinode may have
|
|
Packit |
6ef888 |
deemed to be bad and deleted/freed in pass1. In that case, pass1b
|
|
Packit |
6ef888 |
wouldn't discover the correct [deleted] original reference. In
|
|
Packit |
6ef888 |
that case, we don't want to be confused and consider this second
|
|
Packit |
6ef888 |
reference the same as the first. If we do, we'll never be able to
|
|
Packit |
6ef888 |
resolve it. The first reference can't be the second reference. */
|
|
Packit |
6ef888 |
if (id && first && !(dt->dup_flags & DUPFLAG_REF1_FOUND)) {
|
|
Packit |
6ef888 |
log_info(_("Original reference to block %llu (0x%llx) was "
|
|
Packit |
6ef888 |
"either found to be bad and deleted, or else "
|
|
Packit |
6ef888 |
"a duplicate within the same inode.\n"),
|
|
Packit |
6ef888 |
(unsigned long long)block,
|
|
Packit |
6ef888 |
(unsigned long long)block);
|
|
Packit |
6ef888 |
log_info(_("I'll consider the reference from inode %llu "
|
|
Packit |
6ef888 |
"(0x%llx) the first reference.\n"),
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit |
6ef888 |
dt->dup_flags |= DUPFLAG_REF1_IS_DUPL;
|
|
Packit |
6ef888 |
dt->refs++;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* The first time this is called from pass1 is actually the second
|
|
Packit |
6ef888 |
reference. When we go back in pass1b looking for the original
|
|
Packit |
6ef888 |
reference, we don't want to increment the reference count because
|
|
Packit |
6ef888 |
it's already accounted for. */
|
|
Packit |
6ef888 |
if (first) {
|
|
Packit |
6ef888 |
dt->dup_flags |= DUPFLAG_REF1_FOUND;
|
|
Packit |
6ef888 |
dups_found_first++; /* We found another first ref. */
|
|
Packit |
6ef888 |
} else {
|
|
Packit |
6ef888 |
dt->refs++;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (id == NULL) {
|
|
Packit |
6ef888 |
/* Check for the inode on the invalid inode reference list. */
|
|
Packit |
6ef888 |
int q;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
id = calloc(1, sizeof(*id));
|
|
Packit |
6ef888 |
if (!id) {
|
|
Packit |
6ef888 |
log_crit( _("Unable to allocate inode_with_dups structure\n"));
|
|
Packit |
6ef888 |
return meta_error;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
id->block_no = ip->i_di.di_num.no_addr;
|
|
Packit |
6ef888 |
q = bitmap_type(ip->i_sbd, ip->i_di.di_num.no_addr);
|
|
Packit |
6ef888 |
/* If it's an invalid dinode, put it first on the invalid
|
|
Packit |
6ef888 |
inode reference list otherwise put it on the normal list. */
|
|
Packit |
6ef888 |
if (!inode_valid || q == GFS2_BLKST_UNLINKED)
|
|
Packit |
6ef888 |
osi_list_add_prev(&id->list, &dt->ref_invinode_list);
|
|
Packit |
6ef888 |
else {
|
|
Packit |
6ef888 |
/* If this is a system dinode, we want the duplicate
|
|
Packit |
6ef888 |
processing to find it first. That way references
|
|
Packit |
6ef888 |
from inside journals, et al, will take priority.
|
|
Packit |
6ef888 |
We don't want to delete journals in favor of dinodes
|
|
Packit |
6ef888 |
that reference a block inside a journal. */
|
|
Packit |
6ef888 |
if (fsck_system_inode(ip->i_sbd, id->block_no))
|
|
Packit |
6ef888 |
osi_list_add(&id->list, &dt->ref_inode_list);
|
|
Packit |
6ef888 |
else
|
|
Packit |
6ef888 |
osi_list_add_prev(&id->list,
|
|
Packit |
6ef888 |
&dt->ref_inode_list);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
id->reftypecount[reftype]++;
|
|
Packit |
6ef888 |
id->dup_count++;
|
|
Packit |
6ef888 |
log_info( _("Found %d reference(s) to block %llu"
|
|
Packit |
6ef888 |
" (0x%llx) as %s in %s inode #%llu (0x%llx)\n"),
|
|
Packit |
6ef888 |
id->dup_count, (unsigned long long)block,
|
|
Packit |
6ef888 |
(unsigned long long)block, reftypes[reftype],
|
|
Packit |
6ef888 |
inode_valid ? _("valid") : _("invalid"),
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr,
|
|
Packit |
6ef888 |
(unsigned long long)ip->i_di.di_num.no_addr);
|
|
Packit |
6ef888 |
if (first)
|
|
Packit |
6ef888 |
log_info( _("This is the original reference.\n"));
|
|
Packit |
6ef888 |
else {
|
|
Packit |
6ef888 |
/* Check for duplicate refs to the same block in one inode. */
|
|
Packit |
6ef888 |
if (id->dup_count > 1)
|
|
Packit |
6ef888 |
dt->dup_flags |= DUPFLAG_REF1_FOUND;
|
|
Packit |
6ef888 |
log_info( _("This brings the total to: %d inode references, "
|
|
Packit |
6ef888 |
"%d from this inode.\n"),
|
|
Packit |
6ef888 |
dt->refs, id->dup_count);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return meta_is_good;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
struct dir_info *dirtree_insert(struct gfs2_inum inum)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct osi_node **newn = &dirtree.osi_node, *parent = NULL;
|
|
Packit |
6ef888 |
struct dir_info *data;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* Figure out where to put new node */
|
|
Packit |
6ef888 |
while (*newn) {
|
|
Packit |
6ef888 |
struct dir_info *cur = (struct dir_info *)*newn;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
parent = *newn;
|
|
Packit |
6ef888 |
if (inum.no_addr < cur->dinode.no_addr)
|
|
Packit |
6ef888 |
newn = &((*newn)->osi_left);
|
|
Packit |
6ef888 |
else if (inum.no_addr > cur->dinode.no_addr)
|
|
Packit |
6ef888 |
newn = &((*newn)->osi_right);
|
|
Packit |
6ef888 |
else
|
|
Packit |
6ef888 |
return cur;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
data = calloc(1, sizeof(struct dir_info));
|
|
Packit |
6ef888 |
if (!data) {
|
|
Packit |
6ef888 |
log_crit( _("Unable to allocate dir_info structure\n"));
|
|
Packit |
6ef888 |
return NULL;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
/* Add new node and rebalance tree. */
|
|
Packit |
6ef888 |
data->dinode.no_addr = inum.no_addr;
|
|
Packit |
6ef888 |
data->dinode.no_formal_ino = inum.no_formal_ino;
|
|
Packit |
6ef888 |
osi_link_node(&data->node, parent, newn);
|
|
Packit |
6ef888 |
osi_insert_color(&data->node, &dirtree);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
return data;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
struct dir_info *dirtree_find(uint64_t block)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct osi_node *node = dirtree.osi_node;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
while (node) {
|
|
Packit |
6ef888 |
struct dir_info *data = (struct dir_info *)node;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (block < data->dinode.no_addr)
|
|
Packit |
6ef888 |
node = node->osi_left;
|
|
Packit |
6ef888 |
else if (block > data->dinode.no_addr)
|
|
Packit |
6ef888 |
node = node->osi_right;
|
|
Packit |
6ef888 |
else
|
|
Packit |
6ef888 |
return data;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return NULL;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
/* get_ref_type - figure out if all duplicate references from this inode
|
|
Packit |
6ef888 |
are the same type, and if so, return the type. */
|
|
Packit |
6ef888 |
enum dup_ref_type get_ref_type(struct inode_with_dups *id)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
enum dup_ref_type t, i;
|
|
Packit |
6ef888 |
int found_type_with_ref;
|
|
Packit |
6ef888 |
int found_other_types;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
for (t = ref_as_data; t < ref_types; t++) {
|
|
Packit |
6ef888 |
found_type_with_ref = 0;
|
|
Packit |
6ef888 |
found_other_types = 0;
|
|
Packit |
6ef888 |
for (i = ref_as_data; i < ref_types; i++) {
|
|
Packit |
6ef888 |
if (id->reftypecount[i]) {
|
|
Packit |
6ef888 |
if (t == i)
|
|
Packit |
6ef888 |
found_type_with_ref = 1;
|
|
Packit |
6ef888 |
else
|
|
Packit |
6ef888 |
found_other_types = 1;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
if (found_type_with_ref)
|
|
Packit |
6ef888 |
return found_other_types ? ref_types : t;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return ref_types;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
void dup_listent_delete(struct duptree *dt, struct inode_with_dups *id)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
log_err( _("Removing duplicate reference to block %llu (0x%llx) "
|
|
Packit |
6ef888 |
"referenced as %s by dinode %llu (0x%llx)\n"),
|
|
Packit |
6ef888 |
(unsigned long long)dt->block, (unsigned long long)dt->block,
|
|
Packit |
6ef888 |
reftypes[get_ref_type(id)], (unsigned long long)id->block_no,
|
|
Packit |
6ef888 |
(unsigned long long)id->block_no);
|
|
Packit |
6ef888 |
dt->refs--; /* one less reference */
|
|
Packit |
6ef888 |
if (id->name)
|
|
Packit |
6ef888 |
free(id->name);
|
|
Packit |
6ef888 |
osi_list_del(&id->list);
|
|
Packit |
6ef888 |
free(id);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
void dup_delete(struct duptree *dt)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct inode_with_dups *id;
|
|
Packit |
6ef888 |
osi_list_t *tmp;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
while (!osi_list_empty(&dt->ref_invinode_list)) {
|
|
Packit |
6ef888 |
tmp = (&dt->ref_invinode_list)->next;
|
|
Packit |
6ef888 |
id = osi_list_entry(tmp, struct inode_with_dups, list);
|
|
Packit |
6ef888 |
dup_listent_delete(dt, id);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
while (!osi_list_empty(&dt->ref_inode_list)) {
|
|
Packit |
6ef888 |
tmp = (&dt->ref_inode_list)->next;
|
|
Packit |
6ef888 |
id = osi_list_entry(tmp, struct inode_with_dups, list);
|
|
Packit |
6ef888 |
dup_listent_delete(dt, id);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
osi_erase(&dt->node, &dup_blocks);
|
|
Packit |
6ef888 |
free(dt);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
void dirtree_delete(struct dir_info *b)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
osi_erase(&b->node, &dirtree);
|
|
Packit |
6ef888 |
free(b);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
uint64_t find_free_blk(struct gfs2_sbd *sdp)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct osi_node *n, *next = NULL;
|
|
Packit |
6ef888 |
struct rgrp_tree *rl = NULL;
|
|
Packit |
6ef888 |
struct gfs2_rindex *ri;
|
|
Packit |
6ef888 |
struct gfs2_rgrp *rg;
|
|
Packit |
6ef888 |
unsigned int block, bn = 0, x = 0, y = 0;
|
|
Packit |
6ef888 |
unsigned int state;
|
|
Packit |
6ef888 |
struct gfs2_buffer_head *bh;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
memset(&rg, 0, sizeof(rg));
|
|
Packit |
6ef888 |
for (n = osi_first(&sdp->rgtree); n; n = next) {
|
|
Packit |
6ef888 |
next = osi_next(n);
|
|
Packit |
6ef888 |
rl = (struct rgrp_tree *)n;
|
|
Packit |
6ef888 |
if (rl->rg.rg_free)
|
|
Packit |
6ef888 |
break;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (n == NULL)
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
ri = &rl->ri;
|
|
Packit |
6ef888 |
rg = &rl->rg;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
for (block = 0; block < ri->ri_length; block++) {
|
|
Packit |
6ef888 |
bh = rl->bits[block].bi_bh;
|
|
Packit |
6ef888 |
x = (block) ? sizeof(struct gfs2_meta_header) : sizeof(struct gfs2_rgrp);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
for (; x < sdp->bsize; x++)
|
|
Packit |
6ef888 |
for (y = 0; y < GFS2_NBBY; y++) {
|
|
Packit |
6ef888 |
state = (bh->b_data[x] >> (GFS2_BIT_SIZE * y)) & 0x03;
|
|
Packit |
6ef888 |
if (state == GFS2_BLKST_FREE)
|
|
Packit |
6ef888 |
return ri->ri_data0 + bn;
|
|
Packit |
6ef888 |
bn++;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
return 0;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
uint64_t *get_dir_hash(struct gfs2_inode *ip)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
unsigned hsize = (1 << ip->i_di.di_depth) * sizeof(uint64_t);
|
|
Packit |
6ef888 |
int ret;
|
|
Packit |
6ef888 |
uint64_t *tbl = malloc(hsize);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (tbl == NULL)
|
|
Packit |
6ef888 |
return NULL;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
ret = gfs2_readi(ip, tbl, 0, hsize);
|
|
Packit |
6ef888 |
if (ret != hsize) {
|
|
Packit |
6ef888 |
free(tbl);
|
|
Packit |
6ef888 |
return NULL;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
return tbl;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
void delete_all_dups(struct gfs2_inode *ip)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
struct osi_node *n, *next;
|
|
Packit |
6ef888 |
struct duptree *dt;
|
|
Packit |
6ef888 |
osi_list_t *tmp, *x;
|
|
Packit |
6ef888 |
struct inode_with_dups *id;
|
|
Packit |
6ef888 |
int found;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
for (n = osi_first(&dup_blocks); n; n = next) {
|
|
Packit |
6ef888 |
next = osi_next(n);
|
|
Packit |
6ef888 |
dt = (struct duptree *)n;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
found = 0;
|
|
Packit |
6ef888 |
id = NULL;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
osi_list_foreach_safe(tmp, &dt->ref_invinode_list, x) {
|
|
Packit |
6ef888 |
id = osi_list_entry(tmp, struct inode_with_dups, list);
|
|
Packit |
6ef888 |
if (id->block_no == ip->i_di.di_num.no_addr) {
|
|
Packit |
6ef888 |
dup_listent_delete(dt, id);
|
|
Packit |
6ef888 |
found = 1;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
osi_list_foreach_safe(tmp, &dt->ref_inode_list, x) {
|
|
Packit |
6ef888 |
id = osi_list_entry(tmp, struct inode_with_dups, list);
|
|
Packit |
6ef888 |
if (id->block_no == ip->i_di.di_num.no_addr) {
|
|
Packit |
6ef888 |
dup_listent_delete(dt, id);
|
|
Packit |
6ef888 |
found = 1;
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
if (!found)
|
|
Packit |
6ef888 |
continue;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (dt->refs == 0) {
|
|
Packit |
6ef888 |
log_debug(_("This was the last reference: 0x%llx is "
|
|
Packit |
6ef888 |
"no longer a duplicate.\n"),
|
|
Packit |
6ef888 |
(unsigned long long)dt->block);
|
|
Packit |
6ef888 |
dup_delete(dt); /* not duplicate now */
|
|
Packit |
6ef888 |
} else {
|
|
Packit |
6ef888 |
log_debug(_("%d references remain to 0x%llx\n"),
|
|
Packit |
6ef888 |
dt->refs, (unsigned long long)dt->block);
|
|
Packit |
6ef888 |
if (dt->refs > 1)
|
|
Packit |
6ef888 |
continue;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
id = NULL;
|
|
Packit |
6ef888 |
osi_list_foreach(tmp, &dt->ref_invinode_list)
|
|
Packit |
6ef888 |
id = osi_list_entry(tmp,
|
|
Packit |
6ef888 |
struct inode_with_dups,
|
|
Packit |
6ef888 |
list);
|
|
Packit |
6ef888 |
osi_list_foreach(tmp, &dt->ref_inode_list)
|
|
Packit |
6ef888 |
id = osi_list_entry(tmp,
|
|
Packit |
6ef888 |
struct inode_with_dups,
|
|
Packit |
6ef888 |
list);
|
|
Packit |
6ef888 |
if (id)
|
|
Packit |
6ef888 |
log_debug("Last reference is from inode "
|
|
Packit |
6ef888 |
"0x%llx\n",
|
|
Packit |
6ef888 |
(unsigned long long)id->block_no);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
void print_pass_duration(const char *name, struct timeval *start)
|
|
Packit |
6ef888 |
{
|
|
Packit |
6ef888 |
char duration[17] = ""; /* strlen("XXdXXhXXmXX.XXXs") + 1 */
|
|
Packit |
6ef888 |
struct timeval end, diff;
|
|
Packit |
6ef888 |
unsigned d, h, m, s;
|
|
Packit |
6ef888 |
char *p = duration;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
gettimeofday(&end, NULL);
|
|
Packit |
6ef888 |
timersub(&end, start, &diff);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
s = diff.tv_sec % 60;
|
|
Packit |
6ef888 |
diff.tv_sec /= 60;
|
|
Packit |
6ef888 |
m = diff.tv_sec % 60;
|
|
Packit |
6ef888 |
diff.tv_sec /= 60;
|
|
Packit |
6ef888 |
h = diff.tv_sec % 24;
|
|
Packit |
6ef888 |
d = diff.tv_sec / 24;
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
if (d)
|
|
Packit |
6ef888 |
p += snprintf(p, 4, "%ud", d > 99 ? 99U : d);
|
|
Packit |
6ef888 |
if (h)
|
|
Packit |
6ef888 |
p += snprintf(p, 4, "%uh", h);
|
|
Packit |
6ef888 |
if (m)
|
|
Packit |
6ef888 |
p += snprintf(p, 4, "%um", m);
|
|
Packit |
6ef888 |
|
|
Packit |
6ef888 |
snprintf(p, 8, "%u.%03lus", s, diff.tv_usec / 1000);
|
|
Packit |
6ef888 |
log_notice(_("%s completed in %s\n"), name, duration);
|
|
Packit |
6ef888 |
}
|
|
Packit |
6ef888 |
|