lib/crc32: don't require to modify GPT header

This patch introduces smart crc32 function that is able to exclude
specified. The advantage is that we does not have to modify GPT header
(set the current in-header crc field to zero) when we count crc32.

This allows to keep GPT header in read-only buffers and simplify code.

Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Karel Zak 2015-09-22 15:26:36 +02:00
parent aab9be66c4
commit 7020de0be8
4 changed files with 71 additions and 56 deletions

View File

@ -5,6 +5,8 @@
#include <stdint.h>
extern uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len);
extern uint32_t crc32_exclude_offset(uint32_t seed, const unsigned char *buf, size_t len,
size_t exclude_off, size_t exclude_len);
#endif

View File

@ -98,6 +98,11 @@ static const uint32_t crc32_tab[] = {
0x2d02ef8dL
};
static inline uint32_t crc32_add_char(uint32_t crc, unsigned char c)
{
return crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8);
}
/*
* This a generic crc32() function, it takes seed as an argument,
* and does __not__ xor at the end. Then individual users can do
@ -109,10 +114,29 @@ uint32_t crc32(uint32_t seed, const unsigned char *buf, size_t len)
const unsigned char *p = buf;
while (len) {
crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8);
crc = crc32_add_char(crc, *p++);
len--;
}
return crc;
}
uint32_t crc32_exclude_offset(uint32_t seed, const unsigned char *buf, size_t len,
size_t exclude_off, size_t exclude_len)
{
uint32_t crc = seed;
const unsigned char *p = buf;
size_t i;
for (i = 0; i < len; i++) {
unsigned char x = *p++;
if (i >= exclude_off && i < exclude_off + exclude_len)
x = 0;
crc = crc32_add_char(crc, x);
}
return crc;
}

View File

@ -102,9 +102,10 @@ struct gpt_entry {
/*
* EFI uses crc32 with ~0 seed and xor's with ~0 at the end.
*/
static inline uint32_t count_crc32(const unsigned char *buf, size_t len)
static inline uint32_t count_crc32(const unsigned char *buf, size_t len,
size_t exclude_off, size_t exclude_len)
{
return (crc32(~0L, buf, len) ^ ~0L);
return (crc32_exclude_offset(~0L, buf, len, exclude_off, exclude_len) ^ ~0L);
}
static inline unsigned char *get_lba_buffer(blkid_probe pr,
@ -207,7 +208,7 @@ static struct gpt_header *get_gpt_header(
uint64_t lastlba)
{
struct gpt_header *h;
uint32_t crc, orgcrc;
uint32_t crc;
uint64_t lu, fu;
size_t esz;
uint32_t hsz, ssz;
@ -231,12 +232,11 @@ static struct gpt_header *get_gpt_header(
return NULL;
/* Header has to be verified when header_crc32 is zero */
orgcrc = h->header_crc32;
h->header_crc32 = 0;
crc = count_crc32((unsigned char *) h, hsz);
h->header_crc32 = orgcrc;
crc = count_crc32((unsigned char *) h, hsz,
offsetof(struct gpt_header, header_crc32),
sizeof(h->header_crc32));
if (crc != le32_to_cpu(orgcrc)) {
if (crc != le32_to_cpu(h->header_crc32)) {
DBG(LOWPROBE, ul_debug("GPT header corrupted"));
return NULL;
}
@ -289,7 +289,7 @@ static struct gpt_header *get_gpt_header(
}
/* Validate entries */
crc = count_crc32((unsigned char *) *ents, esz);
crc = count_crc32((unsigned char *) *ents, esz, 0, 0);
if (crc != le32_to_cpu(h->partition_entry_array_crc32)) {
DBG(LOWPROBE, ul_debug("GPT entries corrupted"));
return NULL;

View File

@ -845,11 +845,31 @@ fail:
return NULL;
}
static inline uint32_t count_crc32(const unsigned char *buf, size_t len)
static inline uint32_t count_crc32(const unsigned char *buf, size_t len,
size_t ex_off, size_t ex_len)
{
return (crc32(~0L, buf, len) ^ ~0L);
return (crc32_exclude_offset(~0L, buf, len, ex_off, ex_len) ^ ~0L);
}
static inline uint32_t gpt_header_count_crc32(struct gpt_header *header)
{
return count_crc32((unsigned char *) header, /* buffer */
le32_to_cpu(header->size), /* size of buffer */
offsetof(struct gpt_header, crc32), /* exclude */
sizeof(header->crc32)); /* size of excluded area */
}
static inline uint32_t gpt_entryarr_count_crc32(struct gpt_header *header, struct gpt_entry *ents)
{
size_t arysz = 0;
arysz = le32_to_cpu(header->npartition_entries) *
le32_to_cpu(header->sizeof_partition_entry);
return count_crc32((unsigned char *) ents, arysz, 0, 0);
}
/*
* Recompute header and partition array 32bit CRC checksums.
* This function does not fail - if there's corruption, then it
@ -857,24 +877,13 @@ static inline uint32_t count_crc32(const unsigned char *buf, size_t len)
*/
static void gpt_recompute_crc(struct gpt_header *header, struct gpt_entry *ents)
{
uint32_t crc = 0;
size_t entry_sz = 0;
if (!header)
return;
/* header CRC */
header->crc32 = 0;
crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
header->crc32 = cpu_to_le32(crc);
header->partition_entry_array_crc32 =
cpu_to_le32( gpt_entryarr_count_crc32(header, ents) );
/* partition entry array CRC */
header->partition_entry_array_crc32 = 0;
entry_sz = le32_to_cpu(header->npartition_entries) *
le32_to_cpu(header->sizeof_partition_entry);
crc = count_crc32((unsigned char *) ents, entry_sz);
header->partition_entry_array_crc32 = cpu_to_le32(crc);
header->crc32 = cpu_to_le32( gpt_header_count_crc32(header) );
}
/*
@ -883,28 +892,20 @@ static void gpt_recompute_crc(struct gpt_header *header, struct gpt_entry *ents)
*/
static int gpt_check_header_crc(struct gpt_header *header, struct gpt_entry *ents)
{
uint32_t crc, orgcrc = le32_to_cpu(header->crc32);
uint32_t orgcrc = le32_to_cpu(header->crc32),
crc = gpt_header_count_crc32(header);
header->crc32 = 0;
crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
header->crc32 = cpu_to_le32(orgcrc);
if (crc == le32_to_cpu(header->crc32))
if (crc == orgcrc)
return 1;
/*
* If we have checksum mismatch it may be due to stale data,
* like a partition being added or deleted. Recompute the CRC again
* and make sure this is not the case.
* If we have checksum mismatch it may be due to stale data, like a
* partition being added or deleted. Recompute the CRC again and make
* sure this is not the case.
*/
if (ents) {
gpt_recompute_crc(header, ents);
orgcrc = le32_to_cpu(header->crc32);
header->crc32 = 0;
crc = count_crc32((unsigned char *) header, le32_to_cpu(header->size));
header->crc32 = cpu_to_le32(orgcrc);
return crc == le32_to_cpu(header->crc32);
return gpt_header_count_crc32(header) == orgcrc;
}
return 0;
@ -917,23 +918,11 @@ static int gpt_check_header_crc(struct gpt_header *header, struct gpt_entry *ent
static int gpt_check_entryarr_crc(struct gpt_header *header,
struct gpt_entry *ents)
{
int ret = 0;
ssize_t entry_sz;
uint32_t crc;
if (!header || !ents)
goto done;
return 0;
entry_sz = le32_to_cpu(header->npartition_entries) *
le32_to_cpu(header->sizeof_partition_entry);
if (!entry_sz)
goto done;
crc = count_crc32((unsigned char *) ents, entry_sz);
ret = (crc == le32_to_cpu(header->partition_entry_array_crc32));
done:
return ret;
return gpt_entryarr_count_crc32(header, ents) ==
le32_to_cpu(header->partition_entry_array_crc32);
}
static int gpt_check_lba_sanity(struct fdisk_context *cxt, struct gpt_header *header)