For educational purpose I try to access the FILE
struct in Rust:
unsafe {
let passwd = libc::fopen("/etc/passwd".to_ptr(), &('r' as libc::c_char));
let fp = &mut *(passwd as *mut MY_FILE);
println!("flags={}, file={}", fp._flags, fp._file);
}
the MY_FILE
struct I obtained by running bindgen on stdio.h (I'm on OS X):
bindgen /usr/include/stdio.h
Somehow _flags
is always 8
for files open in write mode (4 in read mode), so this flags seems off (I tested with a C
code to verify that it indeed is not 4 or 8). The file pointer however seems to be right. What could cause this? Am I extracting the binding from the wrong header file? Is there something I need to add to the #[repr(C,)]
attribute?
Here is the full code including the struct.
This is a follow up question from an earlier question
First, your implementation of
ToPtr
invites unsound code. Reproduced here:This allocates a new
CString
and returns a pointer to its contents, but theCString
is dropped whento_ptr
returns, so this is a dangling pointer. Any dereference of this pointer is undefined behavior. The documentation has a big warning about this, but it's still a very common mistake.One correct way to make a
*const c_char
from a string literal isb"string here\0".as_ptr() as *const c_char
. The string is null terminated and there is no dangling pointer because string literals live for the entire program. If you have a non-constant string to be converted, you must keep theCString
alive while it is being used, like this:Aside: an editor "suggested" (I'm new to Stack Overflow, but it seems more polite to comment rather than try to rewrite my answer) that the above code could be written like this:
While this is technically correct (the best kind of correct), the "temporary drop rules" involved are subtle -- this works because
as_ptr
takes a reference to theCString
(actually a &CStr, because the compiler changes the method chain to CString::new(s).unwrap().deref().as_ptr()) instead of consuming it, and because we have only one C function to call. I don't like to rely on anything that's subtle or non-obvious when writing unsafe code.With that out of the way, I fixed that unsoundness in your code (your calls all use string literals so I just used my first strategy above). I get this output on OSX:
So, this matches your results, right? I also wrote the following C program:
And got the output:
Which seems to vindicate the Rust results. There is a comment at the top of
/usr/include/stdio.h
that says:And looking up those constants:
This seems to match the output we get: 4 for file opened in read mode, 8 for write. So what is the problem here?