File Pointer Status

B

Billy Mays

Newbie question:

Is it possible to determine if a given file pointer (FILE *) is still
open or not? I looked through the stdio functions and the c-faq but
still can't find an answer.
 
B

Billy Mays

No. It's unsafe to do anything with a closed FILE.

In my current situation, I have a file pointer that I do not know the
status. It might still be open or closed. What I'm trying to see is if
there is a way to determine (safely) whether or not an arbitrary file
pointer has already been closed?

Does some internal attribute of the FILE struct itself change to signify
that it is open or closed?
 
S

Seebs

In my current situation, I have a file pointer that I do not know the
status. It might still be open or closed. What I'm trying to see is if
there is a way to determine (safely) whether or not an arbitrary file
pointer has already been closed?
No.

Does some internal attribute of the FILE struct itself change to signify
that it is open or closed?

Conceptually, the FILE * is *freed* when you close it.

It's not just that you can't necessarily tell by looking at it -- it's
that once it's been closed, it's no longer there to look at.

In short, your design is broken. You need to make sure that, once a FILE *
is closed, all references to it are removed or turned into null pointers.

-s
 
M

Moi

In my current situation, I have a file pointer that I do not know the
status. It might still be open or closed. What I'm trying to see is if
there is a way to determine (safely) whether or not an arbitrary file
pointer has already been closed?

Well, *your program* closed it.
FILEs don't get closed by accident.
Make your program remember what it did.
(maybe set the fp to NULL after closing the file)
Does some internal attribute of the FILE struct itself change to signify
that it is open or closed?

fopen() does two things: it allocates the FILE struct,
and opens the file.
(for stdin/stdout/stderr this is a bit
different; they are initialized in a different way)

fclose() does exactly the reverse: it closes the file
and deallocates the memory where fp points to.

After fclose(fp), every reference to *fp is invalid.

HTH,
AvK
 
K

Keith Thompson

Billy Mays said:
In my current situation, I have a file pointer that I do not know the
status. It might still be open or closed. What I'm trying to see is if
there is a way to determine (safely) whether or not an arbitrary file
pointer has already been closed?

There isn't.
Does some internal attribute of the FILE struct itself change to signify
that it is open or closed?

There's no guarantee that FILE is a struct (though it typically is), and
the standard certainly says nothing about its contents.

An implementation could call malloc() to allocate a FILE object in
fopen() and free() to deallocate it in fclose(). in other words,
once you close the file, the FILE object may no longer exist.
fclose() cannot modify the FILE* value that you pass to it, so
after fclose(f), any reference to f invokes undefined behavior.

If you don't need a portable solution, there *might* be a
system-specific way to get the information you want. But note that
given this:

FILE *f0;
FILE *f1;
f0 = fopen("some_path", "r");
fclose(f0);
f1 = fopen("some_other_path", "r");

f1 might point to the same FILE object that f0 pointed to. (In an
experiment I just tried, it did exactly that.)

There *might* be some system-specific way to do this, but most likely
there isn't.

You need to find a way to keep track of whether the file is open
or closed.
 
S

sandeep

Billy said:
In my current situation, I have a file pointer that I do not know the
status. It might still be open or closed. What I'm trying to see is if
there is a way to determine (safely) whether or not an arbitrary file
pointer has already been closed?

Does some internal attribute of the FILE struct itself change to signify
that it is open or closed?

If you are on a Unix system, you could try the following code.

#include <unistd.h>
#include <errno.h>

/* returns:
* 0 if open
* 1 if closed
* 2 if error
*/
int isOpen(FILE* f)
{
int f1;
return (f1=dup(fileno(f)))!=-1?close(f1),0:
errno==EBADF?1:2;
}
 
B

Ben Pfaff

sandeep said:
If you are on a Unix system, you could try the following code.

#include <unistd.h>
#include <errno.h>

/* returns:
* 0 if open
* 1 if closed
* 2 if error
*/
int isOpen(FILE* f)
{
int f1;
return (f1=dup(fileno(f)))!=-1?close(f1),0:
errno==EBADF?1:2;
}

The call to fileno(f) might segfault.

Even if it doesn't, it doesn't mean that 'f' is a valid FILE.
 
B

Ben Pfaff

Billy Mays said:
No. It's unsafe to do anything with a closed FILE.

In my current situation, I have a file pointer that I do not know the
status. It might still be open or closed. [...]

Right. Like I said, it's unsafe to do anything with a closed
FILE. So if the file might be closed, you can't do anything with
it.
 
S

sandeep

Ben said:
The call to fileno(f) might segfault.

Even if it doesn't, it doesn't mean that 'f' is a valid FILE.

I don't think so, as long as f is not NULL. Try the following test code.

#include <stdio.h>
#include <unistd.h>
#include <errno.h>

/* returns:
* 0 if open
* 1 if closed
* 2 if error
*/
int isOpen(FILE* f)
{
int f1;
return (f1=dup(fileno(f)))!=-1?close(f1),0:
errno==EBADF?1:2;
}

main()
{
FILE *fp = fopen("/dev/null", "r");
printf("%s\n", (char*[]){"open","closed","error"}[isOpen(fp)]);
fclose(fp);
printf("%s\n", (char*[]){"open","closed","error"}[isOpen(fp)]);
}

When I run it I get

open
closed
 
S

Seebs

I don't think so, as long as f is not NULL.

You are, as usual, wrong.

The claim was not that it is impossible for any implementation to ever
do anything but segfault, but that an implementation *might* segfault.

Which it might. It's certainly allowed to; after a FILE * has been
closed, there is no guarantee that any interaction with the object is
valid. It is unallocated memory to which you have an old pointer.

Consider:
void foo(void) {
char *s = malloc(5);
if (s) {
free(s);
s[0] = '\0';
}
}

This code is certainly *allowed* to segfault, even though on many systems
it won't. It's not portable or reliable code. And accessing a FILE *
after it's been closed is no different.

-s
 
K

Keith Thompson

sandeep said:
If you are on a Unix system, you could try the following code.

#include <unistd.h>
#include <errno.h>

/* returns:
* 0 if open
* 1 if closed
* 2 if error
*/
int isOpen(FILE* f)
{
int f1;
return (f1=dup(fileno(f)))!=-1?close(f1),0:
errno==EBADF?1:2;
}

You call the function "isOpen", and it returns 0 if the file is open?

But ok, let's try it:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>

/* returns:
* 0 if open
* 1 if closed
* 2 if error
*/
int isOpen(FILE* f)
{
int f1;
return (f1=dup(fileno(f)))!=-1?close(f1),0:
errno==EBADF?1:2;
}

int main(void)
{
FILE *f0;
FILE *f1;
/*
* Adjust the following if necessary to refer to
* files that exist on your system.
*/
const char *file0 = "/etc/motd";
const char *file1 = "/etc/passwd";
int fclose_result;

puts("Opening f0");
f0 = fopen(file0, "r");
assert(f0 != NULL);
puts("Closing f0");
fclose_result = fclose(f0);
assert(fclose_result == 0);

f1 = fopen(file1, "r");
assert(f1 != NULL);

switch (isOpen(f0)) {
case 0: puts("f0 is open, OOPS!"); break;
case 1: puts("f0 is closed, ok"); break;
case 2: puts("isOpen(f0) reported an error"); break;
}

return 0;
}

On my system, the output is:

Opening f0
Closing f0
f0 is open, OOPS!
 
B

Billy Mays

Ben said:
The call to fileno(f) might segfault.

Even if it doesn't, it doesn't mean that 'f' is a valid FILE.

I don't think so, as long as f is not NULL. Try the following test code.

#include<stdio.h>
#include<unistd.h>
#include<errno.h>

/* returns:
* 0 if open
* 1 if closed
* 2 if error
*/
int isOpen(FILE* f)
{
int f1;
return (f1=dup(fileno(f)))!=-1?close(f1),0:
errno==EBADF?1:2;
}

main()
{
FILE *fp = fopen("/dev/null", "r");
printf("%s\n", (char*[]){"open","closed","error"}[isOpen(fp)]);
fclose(fp);
printf("%s\n", (char*[]){"open","closed","error"}[isOpen(fp)]);
}

When I run it I get

open
closed

Might it be possible that the FILE is lazily free'd rather than
immediately? I know that free'd pointers that were malloc'd can still
temporarily hold the same data.
 
B

Ben Pfaff

sandeep said:
I don't think so, as long as f is not NULL. Try the following test code.

What if, for example, the fclose() function memsets the FILE to
all-zero-bits and frees it? You will detect that it is a valid
FILE because fd 0 is stdin, which can be dup()'d.
 
E

Ersek, Laszlo

No. It's unsafe to do anything with a closed FILE.

Even freopen()?

C99 7.19.5.4 "The freopen function", p4: "The freopen function first
attempts to close any file that is associated with the specified stream."

My English could be failing me, but it says "any" file, not "the" file.

Thanks,
lacos
 
S

Seebs

Even freopen()?

I would think so.
C99 7.19.5.4 "The freopen function", p4: "The freopen function first
attempts to close any file that is associated with the specified stream."
My English could be failing me, but it says "any" file, not "the" file.

I think that's there for cases such as stderr, where someone could argue
that there is no "file" associated with the stream.

-s
 
E

Ersek, Laszlo

Conceptually, the FILE * is *freed* when you close it.

It's not just that you can't necessarily tell by looking at it -- it's
that once it's been closed, it's no longer there to look at.

In short, your design is broken. You need to make sure that, once a
FILE * is closed, all references to it are removed or turned into null
pointers.

FWIW, the situation might be fixable on UNIX(R) if the OP switched to file
descriptors from stdio streams. Calling fstat() on a file descriptor won't
do any harm; the "worst" that can happen is fstat() returning -1 and
setting errno to EBADF. OTOH, if fstat() completes successfully, one can
use fdopen() to get an stdio stream.

Cheers,
lacos
 
B

Ben Pfaff

Ersek said:
FWIW, the situation might be fixable on UNIX(R) if the OP switched to
file descriptors from stdio streams. Calling fstat() on a file
descriptor won't do any harm; the "worst" that can happen is fstat()
returning -1 and setting errno to EBADF. OTOH, if fstat() completes
successfully, one can use fdopen() to get an stdio stream.

This will work, except that if any other file descriptor is
opened after the previous one is closed, the previous file
descriptor might be associated with a new and entirely different
file (or with something that is not a traditional file at all).
 
K

Keith Thompson

Moi said:
Well, *your program* closed it.
FILEs don't get closed by accident.
Make your program remember what it did.
(maybe set the fp to NULL after closing the file)

Yes, his program closed it, but it may have done so via some library
that he doesn't control.

If the OP controls the code that opens and closes the file, he needs
to modify that code so it keeps track of the file's current status.

If he doesn't, then sadly he has a real problem for which the
language offers no solution.

More details might help us to offer a better solution.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,152
Messages
2,570,858
Members
47,398
Latest member
Moorcam

Latest Threads

Top