Toasteplex, libburn, and 64 bit file offsets

When I originally coded Toasteplex, it was (and currently still is) nothing more than a front-end to cdrecord and growisofs. Each burner would have it’s own instance of whichever program was being used at the time. Since then, I’ve found a nice-looking burning library for Linux called libburn, which is part of the libburnia project.

llibburn comes with an example called libburner, which shows basic usage of the library and how to burn/blank/format discs. From that, I wanted to make my own example program that left out all the extra command line argument processing code so I could see just the libburn function calls and how they worked. Everything went well until I went to burn my first disc with it. I kept getting an error that said it couldn’t reserve a track size of 5027876864 bytes. Strange…the iso I was burning was in fact 732909568 bytes long.

Through various searching with gdb, I found that a call to fstat in my own code returned the correct size for the .iso, and stored it in a variable of the type off_t. This is typecasted variable meant to hold file offsets. I stepped through each line after that in my own code, constantly checking the contents of the file size variable. All was well until a certain libburn function call:

burn_fd_source_new(fd, -1, fixed_size);

This assigns the file descriptor for the iso file and the image size to a burn source struct (libburn is written in c, no objects). Here is my gdb output leading up to and including the beginning of that function:

(gdb) p fixed_size
$1 = 732909568
(gdb) step
82 data_src = NULL;
83 if (fd>=0)
84 data_src = burn_fd_source_new(fd, -1, fixed_size);
(gdb) p fixed_size
$2 = 732909568
(gdb) step
burn_fd_source_new (datafd=7, subfd=-1, size=5027876864) at libburn/file.c:165

What!? 732909568 magically becomes 5027876864? Nope. No magic. Turns out the problem was libburn needed a 64 bit off_t to handle files over 2 GB. When the library was compiled, off_t was typecasted as a long long int, which is a 64 bit signed integer in Linux for a 32-bit processor. In my code, I had not specified that I needed 64 bit file offsets, so mine was only a long int, which is 32 bits. Since in the compile stage gcc only has the function declarations to look at, it has no idea that libburn wants a different kind of off_t. It just knows to make sure were using something typed as off_t. It is the already compiled library code that differs, and gcc just links to that. There isn’t any type checking at that point.

Here’s what it looks like in binary as explained to me by Thomas Schmitt (libburn developer):

0010 1011 1010 1111 0101 0000 0000 0000

0000 0000 0000 0000 0000 0000 0000 0001 0010 1011 1010 1111 0101 0000 0000 0000

The extra bit there at the beginning was just part of arbitrary data on the stack. When libburn received the address of my pointer in the stack, it didn’t know I only had 32 bits available. It grabbed 64 bits starting at that address.

It turns out all I needed to fix it was add the following to the top of my source code:

#define _FILE_OFFSET_BITS 64

This tells the compiler to define off_t as a long long int. I had actually figured that out on my own, but I had made a critically stupid mistake. I put this code BELOW all the system includes. They are the ones that need to see these define statements. Thomas helped me out on that, too. He said he usually defines them in the compile command line, and I realized then that it was the order that was breaking it. I should have known that.

Anyway, thanks, Thomas for all the help!


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: