Not the cause of your error (see below) but very odd nevertheless
struct ar_hdr d;
struct ar_hdr* bob = &d;
...
bob = malloc(sizeof(struct ar_hdr));
...
fwrite(bob, sizeof(d),1, fp);
is quite convoluted, I'd just drop everything into d and that's it.
Now, as your ar_hdr looks like this:
struct ar_hdr /* file member header */
{
char ar_name[16]; /* '/' terminated file member name */
char ar_date[12]; /* file member date */
char ar_uid[6] /* file member user identification */
char ar_gid[6] /* file member group identification */
char ar_mode[8] /* file member mode (octal) */
char ar_size[10]; /* file member size */
char ar_fmag[2]; /* header trailer string */
};
The Wikipedia article mentions that a textual header is the most common format being used:
Offset Length Name Format
0 16 File name ASCII
16 12 File modification timestamp Decimal
28 6 Owner ID Decimal
34 6 Group ID Decimal
40 8 File mode Octal
48 10 File size in bytes Decimal
58 2 File magic 0x60 0x0A
which means that fwrite'ing the struct directly to the file is not what you need to do: C strings as produced by individual sprintf's are null terminated, and what's behind the null (up to the length of the array) is garbage. The file format wants space characters instead of that garbage, and also no null's....
Actually I think your initial fprintf was quite close:
fprintf(fp, "%-16s%-12ld%-6ld%-6ld%-8o%-10lld%c%c",
fname, (long)sb.st_mtimespec.tv_sec,
(long)sb.st_uid, (long)sb.st_gid, sb.st_mode, (long long)sb.st_size,0x60,0xa);
- pad the ar_name field to 16
- ar_mode to octal
- added the file magic
As I'm unable to quickly test that here, there may still be some minor issues but it should be pretty close.
Addendum: for this kind of work a tool to look at the file content byte per byte (like od that comes with all linux distros) and a tool to bytewise compare two files (like dhex) are really a must, because just testing what ar is willing to open is just too hard and time-consuming.