-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrong structs dirent, dirent64 #2669
Comments
I m sorry to say but in fact dirent* struct are in fact correct, I invite you to look at the man pages/headers for confirmation. Note that libc is not a "smart wrapper" neither but reflects what the system libc are. |
@devnexen consider this code: // This code for x86_64-unknown-linux-gnu
use std::ffi::CStr;
fn main() {
unsafe {
let dir = libc::opendir(b"/mnt/3\x00" as *const u8 as *const i8);
assert_ne!(dir, std::ptr::null_mut());
loop {
let ent: *mut libc::dirent = libc::readdir(dir);
if ent == std::ptr::null_mut() {
break;
}
let name = CStr::from_ptr((*ent).d_name.as_ptr());
println!("{}", name.to_bytes().len());
println!("{:?}", name);
}
}
} This code correctly handles file names bigger than 256 bytes. Does it invoke undefined behavior? I. e. is reading past formal struct size undefined behavior? |
getdents is not wrapped by libc crate, nor by glibc. So I will have to manually wrap it, this is error-prone |
you can call it via syscall (the syscalls ids are present in libc) and map the linux_dirent* I think. |
I still think that we should research whether accessing |
or you can copy the result into a sized buffer. I disagree removing readdir honestly. |
You mean read result from |
Note that If anyone wants to chime in the discussion feel free :-) |
I don't have all the context to answer this question definitively, but assuming that |
@digama0 , thanks a lot for answer. This is very big gotcha. This means |
I just found that actual |
@RalfJung Could you comment on the correctness of the comment there? My take is that the reason |
Yeah that comment sounds accurate for the current very conservative semantics we have for |
It sounds like we can improve things with a breaking change. If anyone has any proposals, feel free to put up a PR for 1.0. |
@tgross35 , obviously, perfect solution would be waiting for "thin cstr" and making thin cstr (not reference to thin cstr, but unsized thin cstr itself) last member of dirent |
Yeah, I do wish we had that available. Since we don't though, changing to |
This issue is about the name being longer than 256 bytes. I'd like to add that the name could even be shorter than 256 bytes! I have observed this in practice and this is corroborated by this comment in the std library:
I noticed this, because the following code was crashing:
where
Making |
Yeah by wrapping |
Thanks a lot for the suggestion! |
For the record,
|
|
Indeed it does. Thanks! |
Your structs
dirent
,dirent64
are wrong at x86_64-unknown-linux-gnu. Their fieldd_name
contains 256 bytes. But names can be bigger than 256 bytes in NTFS (in Linux). I was able to create NTFS partition in my Linux and then create file with name "ыыыы..." (250 times "ы", this is Cyrillic letter). Such name contains more than 256 bytes (if you have difficulties with reproducing, I can provide more details).So, fix structs. I am not sure how exactly fix should be implemented. Maybe
[c_char; 512]
, but in this case we need to double check what is true upper limit. Maybe we can simply put[c_char; 1]
or[c_char; 0]
. Of course, it would be perfect to makedirent
unsized, but with machine word sized reference. Unfortunately, as well as I know, such unsized structs are not possible in stable RustThe text was updated successfully, but these errors were encountered: