mirror of https://github.com/crash-utility/crash
394 lines
9.1 KiB
C
394 lines
9.1 KiB
C
/*
|
|
* makedumpfile.c
|
|
*
|
|
* This code is for reading a dumpfile ganarated by makedumpfile command.
|
|
*
|
|
* Copyright (C) 2011 NEC Soft, Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* Author: Ken'ichi Ohmichi <oomichi mxs nes nec co jp>
|
|
*/
|
|
|
|
#define _LARGEFILE64_SOURCE 1 /* stat64() */
|
|
#include "defs.h"
|
|
#include "makedumpfile.h"
|
|
#include <byteswap.h>
|
|
|
|
static void flattened_format_get_osrelease(char *);
|
|
|
|
int flattened_format = 0;
|
|
|
|
struct flat_data {
|
|
int64_t off_flattened;
|
|
int64_t off_rearranged; /* offset which will be rearranged. */
|
|
int64_t buf_size;
|
|
};
|
|
|
|
struct all_flat_data {
|
|
unsigned long long num_array;
|
|
struct flat_data *array;
|
|
size_t file_size;
|
|
};
|
|
|
|
struct all_flat_data afd;
|
|
|
|
struct makedumpfile_header fh_save;
|
|
|
|
static int
|
|
is_bigendian(void)
|
|
{
|
|
int i = 0x12345678;
|
|
|
|
if (*(char *)&i == 0x12)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
static unsigned long long
|
|
store_flat_data_array(char *file, struct flat_data **fda)
|
|
{
|
|
int result = FALSE, fd;
|
|
int64_t offset_fdh;
|
|
int64_t offset_report = 0;
|
|
unsigned long long num_allocated = 0;
|
|
unsigned long long num_stored = 0;
|
|
unsigned long long sort_idx;
|
|
unsigned long long size_allocated;
|
|
struct flat_data *ptr = NULL, *cur, *new;
|
|
struct makedumpfile_data_header fdh;
|
|
struct stat64 stat;
|
|
ulonglong pct, last_pct;
|
|
char buf[BUFSIZE];
|
|
ssize_t bytes_read;
|
|
|
|
fd = open(file, O_RDONLY);
|
|
if (fd < 0) {
|
|
error(INFO, "unable to open dump file %s\n", file);
|
|
return -1;
|
|
}
|
|
if (lseek(fd, MAX_SIZE_MDF_HEADER, SEEK_SET) < 0) {
|
|
error(INFO, "%s: seek error (flat format)\n", file);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
if (stat64(file, &stat) < 0) {
|
|
error(INFO, "cannot stat64 %s\n", file);
|
|
return -1;
|
|
}
|
|
|
|
please_wait("sorting flat format data");
|
|
pct = last_pct = 0;
|
|
while (1) {
|
|
if (num_allocated <= num_stored) {
|
|
num_allocated += 100;
|
|
size_allocated = sizeof(struct flat_data)
|
|
* num_allocated;
|
|
new = realloc(ptr, size_allocated);
|
|
if (new == NULL) {
|
|
error(INFO,
|
|
"unable to realloc flat_data structures\n");
|
|
break;
|
|
}
|
|
ptr = new;
|
|
}
|
|
offset_fdh = lseek(fd, 0x0, SEEK_CUR);
|
|
|
|
if ((bytes_read = read(fd, &fdh, sizeof(fdh))) != sizeof(fdh)) {
|
|
if (bytes_read >= 0)
|
|
error(INFO,
|
|
"read error: %s (flat format): truncated/incomplete\n",
|
|
file);
|
|
else
|
|
error(INFO, "read error: %s (flat format)\n", file);
|
|
break;
|
|
}
|
|
if (!is_bigendian()){
|
|
fdh.offset = bswap_64(fdh.offset);
|
|
fdh.buf_size = bswap_64(fdh.buf_size);
|
|
}
|
|
if (fdh.offset == END_FLAG_FLAT_HEADER) {
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
cur = ptr + num_stored;
|
|
sort_idx = num_stored;
|
|
while (sort_idx) {
|
|
new = ptr + --sort_idx;
|
|
if (new->off_rearranged >= fdh.offset) {
|
|
cur->off_flattened = new->off_flattened;
|
|
cur->off_rearranged = new->off_rearranged;
|
|
cur->buf_size = new->buf_size;
|
|
cur = new;
|
|
} else {
|
|
if (CRASHDEBUG(1) && sort_idx + 1 != num_stored) {
|
|
fprintf(fp,
|
|
"makedumpfile: Moved from %lld to %lld\n",
|
|
num_stored, sort_idx + 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
cur->off_flattened = offset_fdh + sizeof(fdh);
|
|
cur->off_rearranged = fdh.offset;
|
|
cur->buf_size = fdh.buf_size;
|
|
num_stored++;
|
|
|
|
pct = (offset_fdh * 100ULL) / stat.st_size;
|
|
if (pct > last_pct) {
|
|
sprintf(buf, "sorting flat format data: %lld%%", (ulonglong)pct);
|
|
please_wait(buf);
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "\n");
|
|
last_pct = pct;
|
|
}
|
|
|
|
if (CRASHDEBUG(1) && (fdh.offset >> 30) > (offset_report >> 30)) {
|
|
fprintf(fp, "makedumpfile: At %lld GiB\n",
|
|
(ulonglong)(fdh.offset >> 30));
|
|
offset_report = fdh.offset;
|
|
}
|
|
|
|
/* seek for next makedumpfile_data_header. */
|
|
if (lseek(fd, fdh.buf_size, SEEK_CUR) < 0) {
|
|
error(INFO, "%s: seek error (flat format)\n", file);
|
|
break;
|
|
}
|
|
}
|
|
please_wait_done();
|
|
|
|
close(fd);
|
|
if (result == FALSE) {
|
|
free(ptr);
|
|
return -1;
|
|
}
|
|
*fda = ptr;
|
|
|
|
return num_stored;
|
|
}
|
|
|
|
static int
|
|
read_all_makedumpfile_data_header(char *file)
|
|
{
|
|
unsigned long long num;
|
|
struct flat_data *fda = NULL;
|
|
long long retval;
|
|
|
|
retval = num = store_flat_data_array(file, &fda);
|
|
if (retval < 0)
|
|
return FALSE;
|
|
|
|
afd.num_array = num;
|
|
afd.array = fda;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
check_flattened_format(char *file)
|
|
{
|
|
int fd, get_osrelease;
|
|
struct stat stat;
|
|
struct makedumpfile_header fh;
|
|
|
|
if (pc->flags2 & GET_OSRELEASE) {
|
|
get_osrelease = TRUE;
|
|
pc->flags2 &= ~GET_OSRELEASE;
|
|
} else
|
|
get_osrelease = FALSE;
|
|
|
|
if (flattened_format)
|
|
goto out;
|
|
|
|
if (file_exists(file, &stat) && S_ISCHR(stat.st_mode))
|
|
goto out;
|
|
|
|
fd = open(file, O_RDONLY);
|
|
if (fd < 0) {
|
|
error(INFO, "unable to open dump file %s\n", file);
|
|
goto out;
|
|
}
|
|
if (read(fd, &fh, sizeof(fh)) < 0) {
|
|
error(INFO, "unable to read dump file %s\n", file);
|
|
close(fd);
|
|
goto out;
|
|
}
|
|
close(fd);
|
|
|
|
if (!is_bigendian()){
|
|
fh.type = bswap_64(fh.type);
|
|
fh.version = bswap_64(fh.version);
|
|
}
|
|
if ((strncmp(fh.signature, MAKEDUMPFILE_SIGNATURE, sizeof(MAKEDUMPFILE_SIGNATURE)) != 0) ||
|
|
(fh.type != TYPE_FLAT_HEADER))
|
|
goto out;
|
|
|
|
if (get_osrelease) {
|
|
flattened_format_get_osrelease(file);
|
|
return;
|
|
}
|
|
|
|
if (!read_all_makedumpfile_data_header(file))
|
|
return;
|
|
|
|
if (CRASHDEBUG(1))
|
|
fprintf(fp, "%s: FLAT\n\n", file);
|
|
|
|
fh_save = fh;
|
|
|
|
flattened_format = TRUE;
|
|
return;
|
|
|
|
out:
|
|
if (get_osrelease)
|
|
pc->flags2 |= GET_OSRELEASE;
|
|
}
|
|
|
|
static int
|
|
read_raw_dump_file(int fd, off_t offset, void *buf, size_t size)
|
|
{
|
|
if (lseek(fd, offset, SEEK_SET) < 0) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "read_raw_dump_file: lseek error (flat format)\n");
|
|
return FALSE;
|
|
}
|
|
if (read(fd, buf, size) < size) {
|
|
if (CRASHDEBUG(1))
|
|
error(INFO, "read_raw_dump_file: read error (flat format)\n");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
read_flattened_format(int fd, off_t offset, void *buf, size_t size)
|
|
{
|
|
unsigned long long index, index_start, index_end;
|
|
int64_t range_start, range_end;
|
|
size_t read_size, remain_size;
|
|
off_t offset_read;
|
|
struct flat_data *ptr;
|
|
|
|
index_start = 0;
|
|
index_end = afd.num_array;
|
|
|
|
while (1) {
|
|
index = (index_start + index_end) / 2;
|
|
ptr = afd.array + index;
|
|
range_start = ptr->off_rearranged;
|
|
range_end = ptr->off_rearranged + ptr->buf_size;
|
|
|
|
if ((range_start <= offset) && (offset < range_end)) {
|
|
/* Found a corresponding array. */
|
|
offset_read = (offset - range_start) + ptr->off_flattened;
|
|
|
|
if (offset + size <= range_end) {
|
|
if (!read_raw_dump_file(fd, offset_read, buf, size))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
|
|
/* Searh other array corresponding to remaining data. */
|
|
read_size = range_end - offset;
|
|
remain_size = size - read_size;
|
|
if (!read_raw_dump_file(fd, offset_read, buf, read_size))
|
|
return FALSE;
|
|
if (!read_flattened_format(fd, offset + read_size,
|
|
(char *)buf + read_size, remain_size))
|
|
return FALSE;
|
|
break;
|
|
|
|
} else if ((index == index_start) &&
|
|
(index_start + 1 == index_end)) {
|
|
/*
|
|
* Try to read not-written area. That is a common case,
|
|
* because the area might be skipped by lseek().
|
|
* This area should be the data filled with zero.
|
|
*/
|
|
ptr = afd.array + index_end;
|
|
if (offset + size <= ptr->off_rearranged) {
|
|
memset(buf, 0x0, size);
|
|
} else {
|
|
read_size = ptr->off_rearranged - offset;
|
|
remain_size = size - read_size;
|
|
memset(buf, 0x0, read_size);
|
|
if (!read_flattened_format(fd,
|
|
offset + read_size,
|
|
(char *)buf + read_size,
|
|
remain_size))
|
|
return FALSE;
|
|
}
|
|
break;
|
|
|
|
} else if (offset < ptr->off_rearranged)
|
|
index_end = index;
|
|
else
|
|
index_start = index;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
int
|
|
is_flattened_format(char *file)
|
|
{
|
|
check_flattened_format(file);
|
|
return flattened_format;
|
|
}
|
|
|
|
void
|
|
dump_flat_header(FILE *ofp)
|
|
{
|
|
int i;
|
|
|
|
fprintf(ofp, "makedumpfile header:\n");
|
|
fprintf(ofp, " signature: \"");
|
|
for (i = 0; i < SIG_LEN_MDF; i++) {
|
|
if (!fh_save.signature[i])
|
|
break;
|
|
fprintf(ofp, "%c", fh_save.signature[i]);
|
|
}
|
|
fprintf(ofp, "\"\n");
|
|
fprintf(ofp, " type: %llx\n", (ulonglong)fh_save.type);
|
|
fprintf(ofp, " version: %llx\n", (ulonglong)fh_save.version);
|
|
|
|
fprintf(ofp, " all_flat_data:\n");
|
|
fprintf(ofp, " num_array: %lld\n", (ulonglong)afd.num_array);
|
|
fprintf(ofp, " array: %lx\n", (ulong)afd.array);
|
|
fprintf(ofp, " file_size: %ld\n\n", (ulong)afd.file_size);
|
|
}
|
|
|
|
static void
|
|
flattened_format_get_osrelease(char *file)
|
|
{
|
|
int c;
|
|
FILE *pipe;
|
|
char buf[BUFSIZE], *p1, *p2;
|
|
|
|
c = strlen("OSRELEASE=");
|
|
sprintf(buf, "/usr/bin/strings -n %d %s", c, file);
|
|
|
|
if ((pipe = popen(buf, "r")) == NULL)
|
|
return;
|
|
|
|
for (c = 0; (c < 100) && fgets(buf, BUFSIZE-1, pipe); c++) {
|
|
if ((p1 = strstr(buf, "OSRELEASE="))) {
|
|
p2 = strstr(p1, "=");
|
|
fprintf(fp, "%s", p2+1);
|
|
flattened_format = TRUE;
|
|
pc->flags2 |= GET_OSRELEASE;
|
|
}
|
|
}
|
|
|
|
pclose(pipe);
|
|
}
|