I have an application that reads an IP and port from a file and then opens a HTTP connection to the address read. To be a little more sure that I'm connecting to the server I want to connect to I only want to trust the contents of the file if it's owned by root and not writable by anyone else.
I could check the owner and mode with fs.stat and then read the file contents but that would introduce a race condition.
Is there a way to atomically check the owner and the mode and read the file contents?
I dont think that with fs.stat you are introducing a race condition because you are only reading it and not writing on it, so i think that the solution that you found is the best one, right now.
You can maybe add a boolean variable to check if a function is still "reading" the file (after reading should set the boolean), to avoid problems.
Related
Is there a way to open a directory and create it if doesn't exist atomically ?
My use case is simple, I use a directory to watch every public key that are allowed to connect to my server, but if the directory doesn't exist I want to create it, unfortunately for now I only see a two step solution, create the path with create_dir_all() and then open it with read_dir() but this create a possible situation where the directory is delete between the two calls (very unlucky in my docker container... but anyway !)
I didn't find any solution in linux to do that and I'm quite surprise cause that a very common operation for file.
I found a related question Create a directory and return a dirfd with `open` but it's focus on file descriptor and so is more specific. The answer seem to say this doesn't prevent race condition, I don't really understand the context, my only concern is to avoid to create the directory and than try to open it and it's fail. It's more for convenience and robustness of code.
this is a very specific question
I'm mainly interested in the open() system calls the happen when running touch ..
So I ran strace touch . and saw that opennat() is called three times.
but I'm not really understanding whats going on; as touch . does not print anything in the console and does not create a new file named "." since "." is a pointer to the current folder and can be seen by running ls -a so nothing is created since that name is already in use.
this is my assumption:
open() is called to check if the specified file name already exits, if a file descriptor is returned this means that the name is already in use and the operation is canceled.
please correct me if I'm wrong.
GNU touch prefers to use a file descriptor when touching files, since it's possible to write touch - > foo and expect the file foo to be touched. As a result, it always tries to open the specified path as a writable file, and if that's possible, it then uses that file descriptor to update the file timestamp.
In this case, it's not possible to open . for writing, so openat returns EISDIR. touch notices that it's a directory, so its call to its internal fdutimensat function gets an invalid file descriptor and falls back to using utimensat instead of futimens.
It isn't the case that the openat call is used to check that the file exists, but instead that using a file descriptor for many operations means that you don't have to deal with path resolution multiple times or handle symlinks, since all of those are resolved when the file descriptor is opened. This is why many long-lived programs choose to open a file descriptor to their current working directory, then change directories, and then use the file descriptor with fchdir to change back. Any pchanges to permissions after the program starts are not a problem.
I have an open std::fs::File, and I want to get it's filename, e.g. as a PathBuf. How do I do that?
The simple solution would be to just save the path used in the call to File::open. Unfortunately, this does not work for me. I am trying to write a program that reads log files, and the program that writes the logs keep changing the filenames as part of it's log rotation. So the file may very well have been renamed since it was opened. This is on Linux, so renaming open files is possible.
How do I get around this issue, and get the current filename of an open file?
On a typical Unix filesystem, a file may have multiple filenames at once, or even none at all. The file metadata is stored in an inode, which has a unique inode number, and this inode number can be linked from any number of directory entries. However, there are no reverse links from the inode back to the directory entries.
Given an open File object in Rust, you can get the inode number using the ino() method. If you know the directory the log file is in, you can use std::fs::read_dir() to iterate over all entries in that directory, and each entry will also have an ino() method, so you can find the one(s) matching your open file object. Of course this approach is subject to race conditions – the directory entry may already be gone again once you try to do anything with it.
On linux, files handles held by the current process can be found under /proc/self/fd. These look and act like symlinks to the original files (though I think they may technically be something else - perhaps someone who knows more can chip in).
You can therefore recover the (possibly changed) file name by constructing the correct path in /proc/self/fd using your file descriptor, and then following the symlink back to the filesystem.
This snippet shows the steps:
use std::fs::read_link;
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
// if f is your std::fs::File
// first construct the path to the symlink under /proc
let path_in_proc = PathBuf::from(format!("/proc/self/fd/{}", f.as_raw_fd()));
// ...and follow it back to the original file
let new_file_name = read_link(path_in_proc).unwrap();
I'm implementing a libfuse filesystem. When a file is opened, I read the file attributes and store them in a hash table keyed with the file handle I generate. This serves two purposes: to maintain a collection of open file handles and to cache the information I retrieve during opening.
Of course, nothing is stopping user code from trying to pass an invalid file handle, i.e. read from a file that's not open.
There are a number of error codes that I can return from the read function, but it's not clear to me which is the one that is expected in such situation.
As you can see in the POSIX standard, the correct value to return would be EBADF:
[EBADF] The fildes argument is not a valid file descriptor open for
reading.
That said, if user code passes an invalid file handle, the Linux kernel will return EBADF to the user before you, or fuse, get any say on the matter.
We have a Job which takes a Back-up of VSAM file followed by standard Delete-Define-Repro of the same VSAM file. To handle the scenario of trying to delete a non-existing file we are following a standard practice to set MAXCC/LASTCC to 0 if Delete returns a non-zero return code and then continue the process as if there are no errors.
But sometimes we are facing a situation where Delete is not working because file is opened by some user or being read in some other Job. In this case Job fails because while Defining a new VSAM file because file is already present (Delete could not purge it).
Any work-arounds for this situation? Or can we force delete a file even if it is held by some other process/user?
Thanks for reading!
You should be able to work out that it would not be a good idea to delete a VSAM file (or any other) whilst it is being used by "something else".
Why don't you test for the specific value from the DELETE?
If you are doing a backup, then delete/define, it would be a really, really good idea to get exclusive control of the file, else something is going to get messed-up.
You could put a DD with DSN being the VSAM file in question with DISP=OLD, so that your job would only be selected when nothing is using the file.
How are you doing the backup? Why are other jobs accessing the file at the same time anyway? Is this in a "test" environment? What type of VSAM file is it? Why are you doing the REPRO, and do you feel that that is the best way to do it?
An actual answer is difficult without knowing all this, and more.