Is it possible to rescue file descriptor from FILE*? - c

I have to use a certain cross-platform library which passes FILE* objects around.
I get a file descriptor from another source (inherited), I want to keep same fd across fork'd processes.
I currently use fdopen to convert a file descriptor to a FILE* object.
My problem is that fclose used to clean up FILE* objects closes connected file descriptor.
I would very much like to keep this file descriptor after it has been used.
is there a way rescue file descriptor from FILE*?
Is there a way to detach it?
Or a way to substitute a file descriptor in FILE* with a dummy?
P.S. this needs to be cross-platform, well across POSIX anyway.

Supposing that fd is your file descriptor and f your FILE* got from it. Maybe something like the following will do the trick:
fd2 = dup(fd);
fclose(f);
dup2(fd2, fd);
close(fd2);

My problem is that fclose used to clean up FILE* objects closes
connected file descriptor.
You could use dup(2) to get a copy of the descriptor. Then the close(2) that fclose(3) does won't do anything.
I need to maintain exact same fd number
Then call dup2 again after fclose: dup2(savedfd, rescuedfd)

When you get a file descriptor from another source, Try to get its filename from that file descriptor. (Some says its possible using platform specific method. -google it.)
Once you get filename then fopen it again and get FILE* and do your work and clean up it using fclose.
Your original fd will not be disturbed.

Here's a non-portable idea (vote if you think this is good/best):
GNU libc provides fopencookie and BSD provides equivalent funopen.
These return real FILE* handle, but implementation is your own:
It is then relatively trivial to map read/write/seek/close functions to underlying system calls:
read/readfn(cookie, buf, size){ return read((int)cookie, buf, size); }
write/writefn(cookie, buf, size) { return write((int)cookie, buf, size); }
seek/seekfn(cookie, offs, arg) { return seek((int)cookie, offs, arg); } // may require arg mapping to whence
close/closefn(cookie) {} // that's the whole point!

Related

Passing a file descriptor from `open_memstream` to `dup2`

I am trying to redirect output from an exec()ed function into a buffer, so I though I would try and use open_memstream to handle the dynamic buffering
I put together this to test it out:
#include <stdio.h>
#include <unistd.h>
int main() {
char* buffer;
size_t buffer_len;
FILE* stream = open_memstream(&buffer, &buffer_len);
if(!stream) perror("Something went wrong with `open_memstream`!");
fflush(stream);
puts("Start");
if(dup2(fileno(stream), STDOUT_FILENO) == -1) perror("Something went wrong!");
puts("Internal");
fclose(stream);
FILE* f = fopen("out.txt", "w+");
fputs(buffer, f);
fclose(f);
}
But running it gives me the error bad file descriptor on dup2, which shouldn't be the case since open_memstream doesn't return NULL which it is supposed to do on error.
Is there something about the implementation of open_memstream that makes it nonviable to manipulate its underlying descriptor? Or am I just being dumb and using a function wrong?
Cheers in advance for any help given, and if this is impossible to do with open_memstream, is there a way to handle it with FILE* instead of using fds directly?
You should check return value (and subsequently errno) after every operation that can go wrong. Here, you are missing a check for fileno(stream) return value.
FILE* stream = open_memstream(&buffer, &buffer_len);
if(!stream) perror("Failed to open_memstream");
int fd = fileno(stream);
if (fd == -1) {
perror("Failed to get memstream fileno");
exit(1);
}
When you add the above, your program will fail with message
Failed to get memstream fileno: Bad file descriptor
The reason for this failure is already explained in comments on the question.
Have look at open with the O_TMPFILE parameter, or at memfd_create, which is similar to open_memstream but returns a file descriptor.
These approaches force you to forgo the convenience of having &buffer, &buffer_len. But nothing is actually lost. One can use lseek to learn the tmp file size and then mmap to access it as a memory buffer, getting all the conveniences back.

Create GNU C File Descriptor Without File Handle

If I want to use a physical file along with other types of streams such as a socket, I can simply convert a file handle into a file descriptor:
#include <stdlib.h>
#include <stdio.h>
int main(void) {
FILE *f = fopen("uniquefilename.ext", "w");
int fd = fileno(f);
printf("%d\n", fd);
fclose(f);
return 0;
}
Does the GNU Standard Library provide a way to obtain a physical file's descriptor directly? Something to the effect of:
int fd = some_call("file_name.ext", "mode");
It seems I need to note I am completely aware of how a descriptor is not implicitly bound to any specific file. I was misleading when I wrote "obtain a physical file's descriptor"; what I should have wrote is something like "create a descriptor enabling access to a specific physical file".
It does not.
However, you can use the open function directly! This is part of Linux itself, not the C standard library (technically the C standard library provides a small wrapper to allow you to call it as a C function).
Example usage:
int fd = open("file_name.ext", O_RDWR); // not fopen
// do stuff with fd
close(fd); // not fclose
Note: The man page recommends including <sys/types.h>, <sys/stat.h>, and <fcntl.h>, and for close you need <unistd.h>. That's quite a few headers, and I don't know if they're all necessary.

Use opened file descriptor

I've got 2 programs, and in one i'm opening a file to read and from the other one i'm trying to read from file :
first program
fd = open("test.txt",O_RDONLY);
printf("%d\n",fd);
while(1);
second program :
char sir[100];
int fd, result;
scanf("%d",&fd);
rez = read(fd,((void*)sir), 2);
In the second program i read what i printed in first program. Why this code doesn't work and how can i read from that file descriptor from program nr 2?
File descriptors are unique to the process. Also you need to write to the file descriptor.
There are several problems:
fd = open("test.txt", O_RDONLY) opens the file for reading. If I understand what you are trying to do, you want to create the file and open it for writing. That would be fd = open("test.txt", O_CREAT | O_WRONLY).
printf("%d\n",fd) displays the value of the file handle. While that might be useful for debugging, I think you want something which writes to the file handle. write (fd, "hello", 5) is closer to that.
while(1); is an infinite CPU busy loop. This is not very useful.
Similarly the second program has issues:
fd = scanf("%d",&fd) is peculiar. I think you want to open the file just written, no? Instead, fd = open("test.txt", O_RDONLY).
With that corrected, the program can then read the content into the variable read (fd, sir, sizeof sir).
See if those help you.
If you are not primarily working with binary data in the files, the fopen() and fprintf() library calls are more convenient.

How to block file when you reading (fopen) it

How to block file when you reading it (by fopen or open in linux), to prevent any modifications during reading?
What i have: 1 file with data; I want to read data from it in my function, so i use fopen():
FILE *file = fopen(fileName, "r");
Now i need something to block my file - any (or only current user's as variant) another process mustn't have any access (or only modify it as variant) to it until my function will allow them to do it
I suppose, i can do that using set chmod flags for them and setting them back after work;
Or using open() function with special flags arguments, but it's not desirable because i would like to work with fgets() in function;
Is there any examples of how to do it?
Yes, you can use flock to do this. However, since you want to open the file with fopen instead of open, you'll need to first get the file descriptor using fileno. For example:
FILE* f = fopen(...);
int fd = fileno(f);
// flock should return zero on success
flock(fd, LOCK_EX);
That would place an exclusive lock - if you want a shared lock, change LOCK_EX to LOCK_SH.

Why is fileno failing to return a valid descriptor?

I'm opening a stream with funopen
FILE *fin = funopen(cookie, readfn, NULL, NULL, closefn);
if (fin == NULL)
{
handle_error();
return -1;
}
int fdin = fileno(fin);
The call to funopen succeeds but fileno(fin) returns -1.
How can I get the file descriptor? Thanks.
A FILE opened with funopen (which is not part of any standard, by the way; AFAIK it's a BSD extension) does not have an underlying file descriptor. It has the cookie instead. I don't know what you wanted the file descriptor for, but you're probably out of luck.
There's no file connected to funopen, and thus no fd. Try tmpfile instead if you need that.

Resources