Support fstat on non-file-backed FDs#4812
Conversation
|
Thank you for contributing to Miri! A reviewer will take a look at your PR, typically within a week or two. |
This comment has been minimized.
This comment has been minimized.
| let buf = this.deref_pointer(buf_op)?; | ||
|
|
||
| // Extract mode value. write_int will handle size conversion when writing to st_mode field. | ||
| let mode = metadata.mode.to_uint(metadata.mode.size())?.try_into().unwrap_or(0u32); |
There was a problem hiding this comment.
This basically bypasses the sanity check that made has the right type. Why is this needed?
|
Reminder, once the PR becomes ready for a review, use |
|
Hi, thanks for your answer! i’m still here, just a little busy with my work. i will answer to your comments as soon as i can and optimise my PR better. I think it would took around 3 days for me to fix this |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
f39e0b5 to
e825e90
Compare
|
I reworked upd: fixed a cross-host issue that showed up in CI: on Windows hosts running Linux targets, |
|
So I take it you mean this is |
|
That said, with all the @rustbot author |
d67ffc2 to
59e01ba
Compare
|
Reworked approach based on review - replaced the downcast chain and |
|
@rustbot ready |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Sorry for the slow reply. This looks much better, thanks a lot! I still have some suggestions, but we're getting there. :)
@rustbot author
| /// If this FD supports fstat but is not backed by a host file, return the | ||
| /// mode flag name (e.g. "S_IFCHR") for synthetic stat metadata. | ||
| /// FDs that return `None` (default) will use real host file metadata via `metadata()`. | ||
| fn synthetic_stat_mode(&self) -> Option<&'static str> { |
There was a problem hiding this comment.
This API makes me wonder, why are those separate functions? Why don't have have a single function that returns something like Either<io::Result<fs::Metadata>, &'static str>?
Or even better, make it Result<FileMetadata, IoError>. Then file-backed FDs will call FileMetadata::from_meta and others can call FileMetadata::synthetic.
There was a problem hiding this comment.
Done, merged into a single fstat() trait method returning Result<FileMetadata, IoError>. FileHandle calls FileMetadata::from_meta, non-file FDs call FileMetadata::synthetic. FileMetadata is re-exported through unix/mod.rs.
There was a problem hiding this comment.
Now we still have both metadata and fstat. The point was to merge them, to avoid duplication. Does that not work?
There was a problem hiding this comment.
fully merged now. refactored FileMetadata to be cross-platform: mode is stored as &'static str name (e.g. "S_IFREG") and resolved to a target-specific Scalar only at write time; timestamps are stored as raw Option<SystemTime> and converted to Unix-epoch-secs or Windows-epoch-ticks at their respective writers. Single fstat() trait method on FileDescription, no more host_metadata. Windows GetFileInformationByHandle now goes through fstat() too.
There was a problem hiding this comment.
and yes, FileMetadata wasn't cross-platform at all. mode: Scalar came from eval_libc, which panics on Windows. timestamps were kept as (u64, u32) in Unix epoch, Windows uses a different approach. fixed both, now fstat() is the only method and GetFileInformationByHandle goes through it too.
There was a problem hiding this comment.
Ah it's because of Windows support -- makes sense.
Thanks for putting in all this work! However, it's generally better to discuss design decisions with the maintainers and get their feedback before running ahead and implement something suboptimal. FileMetadata is still not target-independent if it has a mode_name field that stores a libc name (which doesn't cover Windows). Having the Windows code match on libc names is an abstraction layering violation.
I'll take over from here to avoid further miscommunication.
| let mode: u16 = metadata | ||
| .mode | ||
| .to_u32()? | ||
| let mode: u16 = (metadata.mode.to_uint(metadata.mode.size())? & u128::from(u16::MAX)) |
There was a problem hiding this comment.
What is the point of this change? (I have asked about this before.)
There was a problem hiding this comment.
Oh you replied elsewhere. Please put the replies in the proper threads so that I have a chance to find them.
Re eval_libc_u32 for mode: mode_t is u16 on macOS in the libc crate, so to_u32() panics. kept eval_libc with Scalar.
Why was this not a problem before? We're already running tests like this on macOS, aren't we?
There was a problem hiding this comment.
But the &-masking still doesn't make sense to me. This should then be
metadata.mode.to_uint(this.libc_ty_layout("mode_t").size)?.try_into().unwrap_or_else(...)There was a problem hiding this comment.
replaced the & u16::MAX mask with libc_ty_layout("mode_t").size to read the scalar at mode_t's actual target width (u16 on macOS, u32 on Linux). same fix applied in the statx writer. sorry about the misplaced reply earlier.
| "S_IFBLK" | ||
| } else { | ||
| "S_IFREG" | ||
| }; |
There was a problem hiding this comment.
Please have a look at this for how to write this case distinction with less code duplication.
There was a problem hiding this comment.
extracted a free function file_type_to_mode_name(FileType) -> &'static str using #[cfg(unix)] per arm, matching the shape of file_type_to_d_type. the cfg_select! block is gone.
| let is_regular_fs_entry = | ||
| file_type.is_file() || file_type.is_dir() || file_type.is_symlink(); | ||
| let (dev, uid, gid) = if is_regular_fs_entry { | ||
| (metadata.dev(), metadata.uid(), metadata.gid()) | ||
| } else { | ||
| (0, 0, 0) | ||
| }; |
There was a problem hiding this comment.
Why do we actively ignore the metadata info from the host here if this is not a regular file/dir/symlink?
There was a problem hiding this comment.
my mistake, removed the is_regular_fs_entry zeroing. from_meta now always use MetadataExt::dev/uid/gid as we always called with real host metadata.
| } | ||
|
|
||
| fn synthetic_stat_mode(&self) -> Option<&'static str> { | ||
| Some("S_IFCHR") |
There was a problem hiding this comment.
We don't actually know that stdin/stdout/stderr are S_IFCHR, right? It could be a file if some redirected the output there.
I'd propose to skip stdin/stdout/stderr in this first PR. We have to either use non-portable APIs to give a correct answer or make a guess, neither of which is great, so until we have a clear usecase IMO it's better to just not support them.
There was a problem hiding this comment.
agreed, dropped stdio support from this PR. Removed the fstat impls on io::Stdin/Stdout/Stderr/NullOutput and the corresponding tests.
|
This PR was rebased onto a different master commit. Here's a range-diff highlighting what actually changed. Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers. |
2eabaf7 to
6aa3ba6
Compare
|
I took the version of this PR before your mos recent changes and did a slightly different refactor., to avoid funneling the Windows logic through something from the unix shims. Thanks a lot for all your help! |
Thank you too for collaborating! It was a pleasure to work on Miri. Will try to find new issues when I have free time and work together again. And about FileMetadata - I agree with your approach, I just thought you knew it's different for Windows and Unix and unifying it was on purpose. |
|
That's fair, but I had indeed entirely forgotten this and was just making guesses in this PR. :) |
Implement fstat support for sockets, pipes, eventfd, epoll, and stdin/stdout/stderr file descriptors. Fixes scalar size mismatch error by using to_uint with scalar's own size instead of to_u32().
Fixes #4753