tags:

views:

391

answers:

3

I have a disk image which contains a standard image using fuse. The Superblock contains the following, and I have a function read_superblock(*buf) that returns the following raw data:

Bytes 0-3: Magic Number (0xC0000112)
      4-7: Block Size (1024)
     8-11: Total file system size (in blocks)
    12-15: FAT length (in blocks)
    16-19: Root Directory (block number)
  20-1023: NOT USED

I am very new to C and to get me started on this project I am curious what is a simple way to read this into a structure or some variables and simply print them out to the screen using printf for debugging.

I was initially thinking of doing something like the following thinking I could see the raw data, but I think this is not the case. There is also no structure and I am trying to read it in as a string which also seems terribly wrong. for me to grab data out of. Is there a way for me to specify the structure and define the number of bytes in each variable?

char *buf;
read_superblock(*buf);
printf("%s", buf);
+4  A: 

Yes, I think you'd be better off reading this into a structure. The fields containing useful data are all 32-bit integers, so you could define a structure that looks like this (using the types defined in the standard header file stdint.h):

typedef struct SuperBlock_Struct {
  uint32_t magic_number;
  uint32_t block_size;
  uint32_t fs_size;
  uint32_t fat_length;
  uint32_t root_dir;
} SuperBlock_t;

You can cast the structure to a char* when calling read_superblock, like this:

SuperBlock_t sb;
read_superblock((char*) &sb);

Now to print out your data, you can make a call like the following:

printf("%d %d %d %d\n",
  sb.magic_number,
  sb.block_size,
  sb.fs_size,
  sb.fat_length,
  sb.root_dir);

Note that you need to be aware of your platform's endianness when using a technique like this, since you're reading integer data (i.e., you may need to swap bytes when reading your data). You should be able to determine that quickly using the magic number in the first field.

Note that it's usually preferable to pass a structure like this without casting it; this allows you to take advantage of the compiler's type-checking and eliminates potential problems that casting may hide. However, that would entail changing your implementation of read_superblock to read data directly into a structure. This is not difficult and can be done using the standard C runtime function fread (assuming your data is in a file, as hinted at in your question), like so:

fread(&sb.magic_number, sizeof(sb.magic_number), 1, fp);
fread(&sb.block_size, sizeof(sb.block_size), 1, fp);
...
Emerick Rogul
Thanks, this was very very helpful.
PKKid
You've not done anything to ensure that the compiler won't insert padding for alignment.
asveikau
I can't, since I don't know what compiler the original poster is using (structure packing is fairly compiler-specific). But you're point's valid; when using the first method I described (casting the structure to char*), typically you must pack the structure to avoid any padding-related problems.
Emerick Rogul
The structure is a good idea, however, each field must be read individually from the input stream (or buffer), due to possible padding between members. There is no standard compiler option for packing structures. Remember: The size of a structure does not equal the sum of the size of its members.
Thomas Matthews
A: 

It's not difficult to print the data after you successfully copied data into a structure Emerick proposed. Suppose the instance of the structure you use to hold data is named SuperBlock_t_Instance.

Then you can print its fields like this:

printf("Magic Number:\t%u\nBlock Size:\t%u\n etc", 
SuperBlock_t_Instance.magic_number, 
SuperBlock_t_Instance.block_size);
Raymond
+1  A: 

Two things to add here:

  1. It's a good idea, when pulling raw data into a struct, to set the struct to have zero padding, even if it's entirely composed of 32-bit unsigned integers. In gcc you do this with #pragma pack(0) before the struct definition and #pragma pack() after it.
  2. For dealing with potential endianness issues, two calls to look at are ntohs() and ntohl(), for 16- and 32-bit values respectively. Note that these swap from network byte order to host byte order; if these are the same (which they aren't on x86-based platforms), they do nothing. You go from host to network byte order with htons() and htonl(). However, since this data is coming from your filesystem and not the network, I don't know if endianness is an issue. It should be easy enough to figure out by comparing the values you expect (e.g. the block size) with the values you get, in hex.
ceo
+1 for structure packing. However as for point 2... I would confirm that the structure in fact is using network byte order. Depending on the format, it may be that the _big-endian_ CPUs are the ones that need to do the swap. (This is common on on-disk structures where the assumption is that x86 will be more common.)
asveikau
-1 for structure packing and -1 for using non-standard functions for Endianness. The Original Poster did not specify the compiler so the pragma may not be available. The ntohs() and ntohl() are not standard functions; again can't get specific because the compiler was not specified.
Thomas Matthews