#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "bblk_einfo.h"
#include "boot_utils.h"
bblk_hash_t bblk_no_hash = {BBLK_NO_HASH, 0, "(no hash)", NULL};
bblk_hash_t bblk_md5_hash = {BBLK_HASH_MD5, 0x10, "MD5", md5_calc};
bblk_hash_t *bblk_hash_list[BBLK_HASH_TOT] = {
&bblk_no_hash,
&bblk_md5_hash
};
static int
einfo_compare_dotted_version(const char *str1, const char *str2)
{
int retval = 0;
char *verstr1, *verstr2, *freeptr1, *freeptr2;
char *parsep1, *parsep2;
unsigned int val_str1, val_str2;
freeptr1 = verstr1 = strdup(str1);
freeptr2 = verstr2 = strdup(str2);
if (verstr1 == NULL || verstr2 == NULL) {
retval = -1;
goto out;
}
while (verstr1 != NULL && verstr2 != NULL) {
parsep1 = strsep(&verstr1, ".");
parsep2 = strsep(&verstr2, ".");
val_str1 = atoi(parsep1);
val_str2 = atoi(parsep2);
if (val_str1 > val_str2) {
retval = 1;
goto out;
}
if (val_str2 > val_str1) {
retval = 2;
goto out;
}
}
if (verstr1 == NULL && verstr2 != NULL)
retval = 2;
if (verstr2 == NULL && verstr1 != NULL)
retval = 1;
out:
free(freeptr1);
free(freeptr2);
return (retval);
}
static int
einfo_compare_timestamps(const char *str1, const char *str2)
{
int retval;
retval = strcmp(str1, str2);
if (retval > 0)
retval = 1;
if (retval < 0)
retval = 2;
return (retval);
}
static int
einfo_compare_version(const char *str1, const char *str2)
{
int retval = 0;
char *verstr1, *verstr2, *freeptr1, *freeptr2;
char *parsep1, *parsep2;
char *timep1, *timep2;
freeptr1 = verstr1 = strdup(str1);
freeptr2 = verstr2 = strdup(str2);
if (verstr1 == NULL || verstr2 == NULL) {
retval = -1;
goto out;
}
timep1 = verstr1;
timep2 = verstr2;
parsep1 = strsep(&timep1, ":");
parsep2 = strsep(&timep2, ":");
while (parsep1 != NULL && parsep2 != NULL) {
parsep1 = strsep(&verstr1, ",-");
parsep2 = strsep(&verstr2, ",-");
if (parsep1 == NULL && parsep2 == NULL)
break;
if (parsep1 == NULL) {
retval = 2;
goto out;
}
if (parsep2 == NULL) {
retval = 1;
goto out;
}
retval = einfo_compare_dotted_version(parsep1, parsep2);
if (retval == 0)
continue;
else
goto out;
}
if (timep1 == NULL || timep2 == NULL) {
retval = -1;
goto out;
}
retval = einfo_compare_timestamps(timep1, timep2);
out:
free(freeptr1);
free(freeptr2);
return (retval);
}
void
print_einfo(uint8_t flags, bblk_einfo_t *einfo, unsigned long bufsize)
{
int i = 0;
char *version;
boolean_t has_hash = B_FALSE;
unsigned char *hash = NULL;
if (einfo->str_off + einfo->str_size > bufsize) {
(void) fprintf(stdout, gettext("String offset %d is beyond the "
"buffer size\n"), einfo->str_off);
return;
}
version = (char *)einfo + einfo->str_off;
if (einfo->hash_type != BBLK_NO_HASH &&
einfo->hash_type < BBLK_HASH_TOT) {
if (einfo->hash_off + einfo->hash_size > bufsize) {
(void) fprintf(stdout, gettext("Warning: hashing "
"present but hash offset %d is beyond the buffer "
"size\n"), einfo->hash_off);
has_hash = B_FALSE;
} else {
hash = (unsigned char *)einfo + einfo->hash_off;
has_hash = B_TRUE;
}
}
if (flags & EINFO_PRINT_HEADER) {
(void) fprintf(stdout, "Boot Block Extended Info Header:\n");
(void) fprintf(stdout, "\tmagic: ");
for (i = 0; i < EINFO_MAGIC_SIZE; i++)
(void) fprintf(stdout, "%c", einfo->magic[i]);
(void) fprintf(stdout, "\n");
(void) fprintf(stdout, "\tversion: %d\n", einfo->version);
(void) fprintf(stdout, "\tflags: %x\n", einfo->flags);
(void) fprintf(stdout, "\textended version string offset: %d\n",
einfo->str_off);
(void) fprintf(stdout, "\textended version string size: %d\n",
einfo->str_size);
(void) fprintf(stdout, "\thashing type: %d (%s)\n",
einfo->hash_type, has_hash ?
bblk_hash_list[einfo->hash_type]->name : "nil");
(void) fprintf(stdout, "\thash offset: %d\n", einfo->hash_off);
(void) fprintf(stdout, "\thash size: %d\n", einfo->hash_size);
}
if (flags & EINFO_EASY_PARSE) {
(void) fprintf(stdout, "%s\n", version);
} else {
(void) fprintf(stdout, "Extended version string: %s\n",
version);
if (has_hash) {
(void) fprintf(stdout, "%s hash: ",
bblk_hash_list[einfo->hash_type]->name);
} else {
(void) fprintf(stdout, "No hashing available\n");
}
}
if (has_hash) {
for (i = 0; i < einfo->hash_size; i++) {
(void) fprintf(stdout, "%02x", hash[i]);
}
(void) fprintf(stdout, "\n");
}
}
static int
compute_hash(bblk_hs_t *hs, unsigned char *dest, bblk_hash_t *hash)
{
if (hs == NULL || dest == NULL || hash == NULL)
return (-1);
hash->compute_hash(dest, hs->src_buf, hs->src_size);
return (0);
}
int
prepare_and_write_einfo(unsigned char *dest, char *infostr, bblk_hs_t *hs,
uint32_t maxsize, uint32_t *used_space)
{
uint16_t hash_size;
uint32_t hash_off;
unsigned char *data;
bblk_einfo_t *einfo = (bblk_einfo_t *)dest;
bblk_hash_t *hashinfo = bblk_hash_list[BBLK_DEFAULT_HASH];
hash_size = hashinfo->size;
hash_off = sizeof (bblk_einfo_t);
if (hash_off + hash_size > maxsize) {
(void) fprintf(stderr, gettext("Unable to add extended info, "
"not enough space\n"));
return (-1);
}
data = dest + hash_off;
if (compute_hash(hs, data, hashinfo) < 0) {
(void) fprintf(stderr, gettext("%s hash operation failed\n"),
hashinfo->name);
einfo->hash_type = bblk_no_hash.type;
einfo->hash_size = bblk_no_hash.size;
} else {
einfo->hash_type = hashinfo->type;
einfo->hash_size = hashinfo->size;
}
(void) memcpy(einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE);
einfo->version = BBLK_EINFO_VERSION;
einfo->flags = 0;
einfo->hash_off = hash_off;
einfo->hash_size = hash_size;
einfo->str_off = einfo->hash_off + einfo->hash_size + 1;
if (infostr == NULL) {
(void) fprintf(stderr, gettext("Unable to add extended info, "
"string is empty\n"));
return (-1);
}
einfo->str_size = strlen(infostr);
if (einfo->str_off + einfo->str_size > maxsize) {
(void) fprintf(stderr, gettext("Unable to add extended info, "
"not enough space\n"));
return (-1);
}
data = dest + einfo->str_off;
(void) memcpy(data, infostr, einfo->str_size);
*used_space = einfo->str_off + einfo->str_size;
return (0);
}
boolean_t
einfo_should_update(bblk_einfo_t *disk_einfo, bblk_hs_t *hs, char *verstr)
{
bblk_hash_t *hashing;
unsigned char *disk_hash;
unsigned char *local_hash;
char *disk_version;
int retval;
if (disk_einfo == NULL)
return (B_TRUE);
if (memcmp(disk_einfo->magic, EINFO_MAGIC, EINFO_MAGIC_SIZE) != 0)
return (B_TRUE);
if (disk_einfo->version < BBLK_EINFO_VERSION)
return (B_TRUE);
disk_version = einfo_get_string(disk_einfo);
retval = einfo_compare_version(verstr, disk_version);
if (retval == -1 || retval == 2)
return (B_FALSE);
if (disk_einfo->hash_type == bblk_no_hash.type)
return (B_TRUE);
if (disk_einfo->hash_type >= BBLK_HASH_TOT)
return (B_TRUE);
hashing = bblk_hash_list[disk_einfo->hash_type];
local_hash = malloc(hashing->size);
if (local_hash == NULL)
return (B_TRUE);
if (compute_hash(hs, local_hash, hashing) < 0) {
free(local_hash);
return (B_FALSE);
}
disk_hash = (unsigned char *)einfo_get_hash(disk_einfo);
if (memcmp(local_hash, disk_hash, disk_einfo->hash_size) == 0) {
free(local_hash);
return (B_FALSE);
}
free(local_hash);
return (B_TRUE);
}
char *
einfo_get_string(bblk_einfo_t *einfo)
{
if (einfo == NULL)
return (NULL);
return ((char *)einfo + einfo->str_off);
}
char *
einfo_get_hash(bblk_einfo_t *einfo)
{
if (einfo == NULL)
return (NULL);
return ((char *)einfo + einfo->hash_off);
}