-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Provide a way of obtaining numeric file descriptors. #15643
Conversation
it is still missing Windows support... and unit tests. Would be great to hear some feedback before I proceed with more work on this. |
My main question regarding the unit tests is whether I should add it to every implementation of this trait except from the trait definition, right? |
Regarding Windows, I have no idea, so please do feel free to make some suggestions... Does it even have FDs in the same sense? |
I'm a little wary to do this because of, sadly, windows. I don't quite grok how windows deals with I would want to understand better how this works with windows before moving forward, personally. |
@alexcrichton understood.
I have briefly though of doing something along the lines of: fn with_c_fd(&self, close_after: bool, block: |libc::c_int| -> IoResult) { match block(self.fd()) { ... } } Although this seems rather useful and makes sense along the side of To me, the most disapointing bit was finding out that there is |
@alexcrichton by looking at There is only an additional method: pub fn fd(&self) -> fd_t { self.inner.fd }
pub fn handle(&self) -> libc::HANDLE {
unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
} So I guess I could provide We need to check with |
I'm not sure whether a file descriptor is a first-class thing in windows or what relation it has to HANDLE other than there's likely a one-to-one mapping. Most windows apis deal with a HANDLE instead of a file descriptor, so this change would definitely be favoring unix over windows. From your example above, however, this is why I've been hesitant to add it in the past. Many methods on these objects won't work if you start manually fiddling with flags on the file descriptor itself. |
Perhaps |
I have considered this and it's in the doc comment. There is not much you
|
This is not necessarily a security bug, it has the possibility of becoming a memory safety bug. Functions in libnative (and possibly libuv as well) may be designed assuming complete control of a file descriptor. If this control is broken, the broken assumptions may lead to memory unsafety. I don't think we have any examples of this today, but that doesn't mean that it's impossible to have. |
OK, sure. I'm not completely opposing making it an unsafe function. And, So best solution would probably be to just simply call those functions
|
For what it's worth, a |
So my Stack Overflow post resulted in a solution that will work for what I am doing right now. |
I do still insist on this going forward. @alexcrichton you commented earlier:
So it does occur to me now, based on the solution posted on Stack Overflow, that Perhaps In fact, one might actually simply use this to could how many files they have opened so far. As far as standard run time goes, it could actually attempt to protect form file descriptor leaks by preventing someone from opening the same file over and over again with the same mode... It would just tell you, that this had already been open and here is the original handle. |
The librustuv and libnative interfaces are likely to always be experimental and not intended for general consumption. They provide their own access to file descriptors out of necessity for the library itself, I wouldn't recommend relying on the native/rustuv interfaces for extracting file descriptors. If we were to add this function, I believe that it should be Also, from what @andrew-d said about file descriptors on windows, this would be tying rust permanently to their C runtime library. I'm not entirely sure if that's desirable or not? |
Another use case for this would when one wants to use Anyhow, right now I need to use extern crate std;
extern crate native;
extern crate debug;
use native::io::file::open;
use std::rt::rtio::{Open, Read};
use std::os::{MemoryMap, MapReadable, MapWritable, MapFd};
fn main() {
let path = "test.bin";
let file =
match open(&path.to_c_str(), Open, Read) {
Err(_) => { fail!("Something is terribly wrong!"); },
Ok(f) => { f },
};
let fmap =
match MemoryMap::new(4096u, [MapReadable, MapWritable, MapFd(file.fd())]) {
Err(_) => { fail!("Something is terribly wrong!"); },
Ok(f) => { f },
};
println!("{:?}", fmap);
} |
Ok, after stewing for a bit I think I'd like to move forward with this. @errordeveloper, could you add the following pieces to the pr?
And some unresolved questions I also have:
Does that sounds ok? We can deal with the questions in time, and I'm around to help out with any questions you have, feel free to reach out on IRC! (I'm acrichto) |
|
I do not like this, Sam-I-Am. Supposing I wish to use a different runtime that doesn’t use that sort of descriptor at all—a bare metal case might do it, or perhaps an emscripten-specific runtime for running in the browser (I can easily imagine that one having absolutely nothing to do with such magic numbers). I do not believe it is at all reasonable to expect to be able to have such a descriptor, at the language level at least. Still, for pragmatism’s sake, it must evidently be exposed somewhere. Given that, I would say very strongly that such functions should be marked unsafe, and probably unstable also. I would think also that having it on a separate trait would be a good thing—could the trait even live in the libc crate to make it absolutely clear that it’s That Sort of Thing? |
A benefit of using a trait for this is that you could then use it as a generic bound, e.g. for exposing a safe interface over |
I cannot disagree with you, Chris. I do however believe that current
|
A trait is useless for distiguishing things that only have sensible implementation under certain runtimes, the |
This is to improve the interoperability with external C libraries. One should be able to leverage most Rust's standard run-time functions for openning and closing files with all the error handling etc, while only making C library calls when it's very neccessary.
@huonw what do you say: add |
I have just notice new rust-lang/rfcs#185... If that's to be implemented, I'd hold on with the |
I don't see why getting the file descriptor should be unsafe – getting a raw pointer from a As per the Rust design documents, reading private values from objects isn't considered unsafe if I remember correctly. |
First, about the Second, it would be safer to enclose the file descriptor use into a closure. What's unsafe about file descriptor is their lifetime: they can be closed by another piece of code, refer to a different file (i.e. I don't think we need a We could use a
I used The problem is that we get |
Not the right choice, prefer to return To properly handle file descriptor lifetime in bindings, the right thing to do is to wrap the bindings in a Rust object who handle the file descriptor closing. |
I don't see how reading the file descriptor is unsafe in any way – only using them to construct other things should be unsafe. Compare
(which is safe) to
(which is unsafe). Getting the raw value of a pointer is not unsafe at all, while actually using it is. This is how it should be handled with file descriptors IMO, getting them out of a structure is no problem at al – only using them to construct other things should be unsafe. |
What are file descriptors supposed to be on Windows? I only know of |
It has numeric file descriptors in the C runtime, which can be converted to and from a |
Anyway, it would be nice to expose a proper pub struct FileDesc {
fd: c_int
}
impl FileDesc {
pub unsafe fn from_handle(fd: c_int) -> FileDesc { ... }
pub fn as_handle(&self) -> c_int { ... }
pub fn into_handle(self) -> c_int { ... }
pub fn dup(&self) -> Result<FileDesc, Error> { ... }
pub fn read(&self, buffer: &mut [u8]) -> IoResult<uint> { ... }
pub fn write(&self, buffer: &[u8]) -> IoResult<uint> { ... }
...
} An important thing to keep in mind is that all files should be atomically |
Preferable to use a dedicated
Like I said previously, we should probably take care of file descriptor type (e.g. regular file, socket, directory) and their capabilities: it should not be possible to attempt a To avoid this, trait BasicFileDesc {
pub unsafe fn from_handle(fd: fd_t) -> Self { … }
// …
pub fn dup(&self) -> IoResult<Self> { … fcntl() … } // also called by clone()?
}
impl BasicFileDesc for RegularFd;
// same for all other file descriptor types…
trait DirFileDesc {
pub fn openat(…) -> IoResult<FileDesc> // enum defined below
pub fn getdents(…) -> IoResult<DirEntry> // or readdir-like
// …
}
impl DirFileDesc for DirectoryFd;
trait RwFileDesc {
pub fn read(…) -> IoResult<uint> { ... }
// …
}
impl RwFileDesc for RegularFd;
impl RwFileDesc for CharDevFd;
// …
trait DevFileDesc {
fn ioctl(…) -> IoResult<()>
// …
}
impl DevFileDesc for CharDevFd;
impl CharDevFd {
fn isatty(…) -> bool { … }
}
enum FileDesc {
Regular(RegularFd),
Directory(DirectoryFd), // implement openat, linkat…
CharDev(CharDevFd), // implement ioctl, isatty…
BlockDev(BlockDevFd), // implement ioctl…
Pipe(PipeFd),
Symlink(SymlinkFd),
Socket(SocketFd),
Unknown(UnknownFd), // seems to be possible: bad thing for static typing but could implement all traits
} E.g. the
Right, |
@l0kod: |
If you do this with an |
Indeed, the whole point we wanted to achieve with this was compatiblity with low-level libraries in other languages – so wrapping it up as such an enum would have the wrong effect in my opinion. |
OK, I missed some file descriptor types ;) but It's probably why |
Doesn't it one of the goal of static typing: to avoid confusion between different things? I don't see why it would be a problem for binding with low-level library. |
Because C doesn't understand these enums, it only "understands" the raw |
Yes, C understand the raw |
By adding abstraction and typing restrictions, it's no longer a low-level library and can no longer express everything that you can in C. Exposing low-level functionality does not preclude having high-level abstractions built on top of them. It shouldn't go beyond what is necessary to achieve safety unless it's not making any use cases more difficult. |
Yes, that's the goal of proper wrapping, to avoid errors :) I don't see the difference of use between a single The idea is that bindings who return file descriptors (e.g. |
Plus to thestinger, I feel the primary goal is a possibility to use an operating system API in its entirety. |
Good news! libuv now support file descriptors: joyent/libuv#1435. |
Of particular note is that libuv provides |
Closing due to inactivity. There are many changes planned to the I/O libraries, especially libnative. While these changes settle down over the coming weeks, something like this will definitely be included. I would recommend holding off to see how the new design of libnative works out. The plan is to have high-level primitives in libstd which provide access to lower-level primitives in libnative which in turn provide access to the raw file descriptor/handle objects. |
@alexcrichton I am definitely looking forward to see those changes. Is there a tracking issues for it? |
I'd follow rust-lang/rfcs#219 which, although closed, will be updated with the next course of action. |
See (and please comment on!) this new RFC. |
Merged RFC: https://github.com/rust-lang/rfcs/blob/master/text/0230-remove-runtime.md Related issue: #17325. |
New PR: #19169 |
This is to improve the interoperability with external C libraries.
One should be able to leverage most Rust's standard run-time functions
for openning and closing files with all the error handling etc, while
only making C library calls when it's very neccessary.