Open new device descriptor with same options as reference descriptor - c

I have an open device descriptor, where I don't know the device name and the options
passed to open(...).
I want to open a new device descriptor with the same options passed to open.
int newFd = copy(referenceFd);
Where copy would do the job. dup() is certainly the wrong choice as a further ioctl() on newFd would also alter the referenceFd, therefore I want to open a new descriptor.
Is there a system call which provides such functionality?
I have not been able to find something yet.

You can probably do it with a series of fcntl calls:
F_GETFD - Get the file descriptor flags defined in that are associated with the file descriptor fildes.
F_GETFL - Get the file status flags and file access modes, defined in , for the file description associated with fildes.
I linked the SUSv4 page above; you might also be interested in the Linux version.

First, get the descriptor flags of file descriptor fd using
int flags = fcntl(fd, F_GETFL);
Then, reopen the descriptor using the Linux-specific /proc/self/fd/fd entry:
int newfd;
char buffer[32];
if (snprintf(buffer, sizeof buffer, "/proc/self/fd/%d", fd) < sizeof buffer) {
do {
newfd = open(buffer, flags & ~(O_TRUNC | O_EXCL), 0666);
} while (newfd == -1 && errno == EINTR);
if (newfd == -1) {
/* Error: Cannot reopen file. Error in errno. */
}
} else {
/* Error: the path does not fit in the buffer. */
}
The reopened file descriptor is then in newfd.
Note that you most likely want to make sure O_TRUNC (truncate the file) and O_EXCL (fail if exists) are not in the flags, to avoid the cases where reopening using the exact original flags would cause unwanted results.
You do not want to use lstat(), because that opens up a race condition with respect to rename -- a user renaming the target file between the lstat() and the open() in above code. The above avoids that completely.
If you don't want to be Linux-specific, add code that tries the same with /dev/fd/fd if the above fails. It is supported in some Unix systems (and most Linux distros too).

Related

How to see the error of open()

I am working with pipes and one pipe won't open, even though mkfifo() was successful.
I have this:
/* create the FIFO (named pipe) */
int ret_mk = mkfifo(out_myfifo, 0666);
if(ret_mk < 0) {
perror(out_myfifo);
unlink(out_myfifo);
return -1;
}
printf("ret_mk = %d\n", ret_mk);
/* write to the FIFO */
out_fd = open(out_myfifo, O_WRONLY);
printf("out_fd = %d\n", out_fd);
but nothing gets printed after open(), even a print of random text won't show up.
From here we have:
The open() function returns an integer value, which is used to refer to the file. If unsuccessful, it returns -1, and sets the global variable errno to indicate the error type.
What can I do to see why it won't open?
Read fifo(7). For FIFOs, an open call may block. To make open(2) non-blocking, use O_NONBLOCK in the flag argument:
out_fd = open(out_myfifo, O_WRONLY|O_NONBLOCK);
if (out_fd<0) perror(out_myfifo);
printf("%d\n", out_fd);
But usually you want a blocking open for write on a FIFO, because some other process should open the same FIFO for reading (and you want your writing process to wait that to happen).
Notice that there is no way to poll(2) the event that someone else has opened the other end of your FIFO (because poll wants an opened file descriptor). See also inotify(7); you could also want to use unix(7) sockets.
BTW, you could also use strace(1) for debugging purposes.
See also intro(2) and Advanced Linux Programming.

create and open returning two different fd descriptors

if((fd = creat(file_name,O_RDWR|S_IRWXU|S_IRWXG|S_IRWXO)) < 0){
perror("Create failed!");
return -1;
}
if((fd = open(file_name,O_CREAT | O_RDWR))< 0){
perror("Open failed!");
return -1;
}
write(fd,buff,100);
Why do the fd's are different for creat and open, as its opening the same file.
I want to create , open and write a file.
I should not use the open(filename,O_CREAT|modes);
The creat function opens the file after creating it, so from your description, simply calling creat() is sufficient for your needs.
To answer your question, it gives you multiple file descriptors because you've performed multiple opens (creat is an open with creation). You can access the same file using both descriptors (and your current location within the file can differ for each descriptor).
Literally creat() function is equivalent to open(pathname, O_RWONLY | O_CREAT | O_TRUNCATE, mode) so your second open() is redundant.
You should also keep in mind that the fd is just a handler of your current process. Different handler may point to the same file as we call it "File Sharing".
Another suggestion is, try
if (write(fd, buff, 100) != 100)
/*Error handling codes*/
to make sure the write() works correctly.

What is the purpose of calling fcntl() be called with the file descriptor as -1 and cmd as F_GETFL?

I am trying to understand what this line of code means:
flags = fcntl(-1,F_GETFL,0);
The usual reason for calling fcntl() with the F_GETFL flag is to modify the flags and set them with fcntl() and F_SETFL; the alternative reason for calling fcntl() with F_GETFL is to find out the characteristics of the file descriptor. You can find the information about which flags can be manipulated by reading (rather carefully) the information about <fcntl.h>. The flags include:
O_APPEND — Set append mode.
O_DSYNC — Write according to synchronized I/O data integrity completion.
O_NONBLOCK — Non-blocking mode.
O_RSYNC — Synchronized read I/O operations.
O_SYNC — Write according to synchronized I/O file integrity completion.
Plus (POSIX 2008) O_ACCMODE which can then be used to distinguish O_RDONLY, O_RDWR, and O_WRONLY, if I'm reading the referenced pages correctly.
However, it makes no sense whatsoever to call fcntl() with a definitively invalid file descriptor such as -1. All that happens is that the function returns -1 indicating failure and sets errno to EBADF (bad file descriptor).
Assuming we are talking about the function described by man 2 fcntl:
flags = fcntl(-1,F_GETFL,0);
tries to perform some action on an invalid file descriptor (-1) and therefore will never do anything else but returning -1 and set errno to EBADF.
I'd say you can savely replace this line by:
flags = -1; errno = EBADF;
The fcntl() function performs various actions on open descriptors. Its syntax is:
int fcntl(int descriptor,
int command,
...)
read about Return Value:
-1 then fcntl() was not successful. The errno global variable is set to indicate the error.
this code:
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
int main(){
int flags;
if((flags = fcntl(-1,F_GETFL,0)) < 0){
perror("fcntl: ");
}
printf("\n %d\n", flags);
}
output is:
~$ gcc xx.c
~$ ./a.out
fcntl: : Bad file descriptor
-1
Notice the printed flags value is -1 that indicates not successful call of fcntl(-1,F_GETFL,0); because -1 is not a valid file descriptor. And valid file descriptors starts from 0. (that is what perror() prints error message Bad file descriptor, EBADF)
note: I run this code in Linux System.
Edit:
F_GETFL is for GET flags command in fcntl().

open with O_CREAT - was it opened or created?

I have 10 processes which try open the same file more or less at the same time using open(O_CREAT) call, then delete it. Is there any robust way to find out which process actually did create the file and which did open already create file, for instance, if I want to accurately count how many times that file was opened in such scenario.
I guess I could put a global mutex on file open operation, and do a sequence of open() calls using O_CREAT and O_EXCL flags, but that doesn't fit my definition of "robust".
Use O_EXCL flag with O_CREAT. This will fail if the file exists and errno will be set to EEXIST. If it does fail
then attempt open again without O_CREAT and without O_EXCL modes.
e.g.
int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0644);
if ((fd == -1) && (EEXIST == errno))
{
/* open the existing file with write flag */
fd = open(path, O_WRONLY);
}
Based roughly on your comments, you want something along the lines of this function:
/* return the fd or negative on error (check errno);
how is 1 if created, or 0 if opened */
int create_or_open (const char *path, int create_flags, int open_flags,
int *how) {
int fd;
create_flags |= (O_CREAT|O_EXCL);
open_flags &= ~(O_CREAT|O_EXCL);
for (;;) {
*how = 1;
fd = open(path, create_flags);
if (fd >= 0) break;
if (errno != EEXIST) break;
*how = 0;
fd = open(path, open_flags);
if (fd >= 0) break;
if (errno != ENOENT) break;
}
return fd;
}
This solution is not bullet proof. There may be cases (symbolic links maybe?) that would cause it to loop forever. Also, it may live-lock in certain concurrency scenarios. I'll leave resolving such issues as an exercise. :-)
In your edited question, you pose:
I have 10 processes which try open the same file more or less at the same time using open(O_CREAT) call, then delete it.
A hack-ish, but more bullet proof, solution would be to give each process a different user ID. Then, just use the regular open(path, O_CREAT|...) call. You can then query the file with fstat() on the file descriptor, and check the st_uid field of the stat structure. If the field equals the processes' user ID, then it was the creator. Otherwise, it was an opener. This works since each process deletes the file after opening.

open file O_NONBLOCKING gets lost in kernel module

I am opening a file in my C program:
pcm->dfd = open(fname, O_RDONLY|O_NONBLOCK);
and later call select() and read() on it.
But my problem is, that the O_NONBLOCK gets lost somewere:
ssize_t my_read(struct file *filp, char __user *user_buffer, size_t bytes_requested, loff_t *capture_ptr) {
if (filp->f_flags & O_NONBLOCK){
LOGI("mode: O_NONBLOCK");
}
else{
LOGI("mode: BLOCKING"); // <-- this is printed
}
..
}
I also tried
pcm->dfd=open(fname, O_RDONLY|O_NONBLOCK);
// O_NONBLOCK does not work :/
int flags = fcntl(pcm->dfd, F_GETFL, 0);
fcntl(pcm->dfd, F_SETFL, flags | O_NONBLOCK);
It's not a logging-problem, the driver also behaves as in blocking-mode.
Anyone an idea?
EDIT:
The code which reads from the opened file is absolutely simple:
size=read(pcm->dfd,inBuffer,inBufferBytes);
I also checked the program if there's a fcntl() somewere else, but no..
EDIT 2:
May it be possible, that the O_NONBLOCK has an other value in my user-program (Android NDK) than in the kernel? I searched for O_NONBLOCK in the kernel-headers and already there are 2 different definitions.
I also checked the open-implementation in my kernel module and already there filp->f_flags is not O_NONBLOCK.
According to open(2) man-page, passing O_NONBLOCK only makes the open call itself non-blocking (which you, probably, don't want). It does not imply, that the opened file descriptor will also be in non-blocking mode -- you have to set that with a fcntl() after opening.

Resources