Home > Software engineering >  Parse Bytes Into Struct In C
Parse Bytes Into Struct In C

Time:03-24

I am trying to parse these 30 bytes:

00000000: 4353 4333 3630 4653 0200 0000 1900 0000
00000010: 0002 0000 0032 0000 0035 0000

Into a struct (also 30 bytes):

struct superblock_t {
uint8_t fs_id [8];
uint16_t block_size;
uint32_t file_system_block_count;
uint32_t fat_start_block;
uint32_t fat_block_count;
uint32_t root_dir_start_block;
uint32_t root_dir_block_count;
} PACKED;

I am first reading the 30 bytes into a buffer then using memcpy() to copy desired memory.

unsigned char buf[30]; //should this buffer be size_t 31?
read(file_descriptor, buf, 30);

struct superblock_t super_block; //initilize a super_block
memcpy(super_block.fs_id, buf, 8);
memcpy(super_block.block_size, buf   8, 2);
memcpy(super_block.file_system_block_count, buf   10, 4);
// and so on for additional attributes.

I am then getting a segfault error :(

Am I misusing the memcpy() function? Sorry, I'm coming from c for reference.

CodePudding user response:

unsigned char buf[30]; //should this buffer be size_t 31?

You can't infer 30 or 31 bytes accurately. In your case, the member fields of the struct The compiler is free (and will) apply padding bytes between members of the struct and/or at the end of the struct.

Given that uint32_t file_system_block_count is preceeded by 10 bytes (not a number divisible by 4), you can't assume that the address of this field is exactly 10 bytes offset from the start of the struct instance. That's a very likely place for padding to exist.

You could to this:

unsigned char buf[sizeof(struct superblock_t)];
read(file_descriptor, buf, sizeof(struct superblock_t));

And that works so long as the file was written with code generated by the same compiler and compiler flags such that the same padding bytes are applied to both the reading and writing code. Another program saving that file, which was compiled differently, might have a different size for superblock. (We could also discuss difference of endianness of integers, but I digress).

A better solution is to always serialize the reading and writing of your struct on a member by member basis

struct superblock_t block = {0};
read(file_descriptor, &block.fs_id, sizeof(block.fs_id));
read(file_descriptor, &block.block_size, sizeof(block.block_size));
read(file_descriptor, &block.file_system_block_count, sizeof(block.file_system_block_count));
...

The above implies you wrote the members out to file using a similar code path that issued individual write calls for each field.

CodePudding user response:

The prototype (found e.g. here https://en.cppreference.com/w/c/string/byte/memcpy ) is

void* memcpy( void *dest, const void *src, size_t count );

It wants pointers for destination.
In more than one case you provide integers, writing at runtime to whatever the compiler understands there does not surprise me by causing a segfault.
And the warnings you quote say the same.

So the answer to your question is yes.
You are misusing the memcpy function.

You need to provide pointers instead of copies.
E.g.:

memcpy(&(super_block.block_size), buf   8, 2); // the generous () for clarity

(Not discussing the assumption-based 2 here. Though I think there are better ways.)

  • Related