First of all, don't mix standard library <stdio.h>
calls (like fprintf(3)
or fopen(3)
) with system calls (like open(2)
or close(2)
or sync(2)
) as the formers are library routines that use in-process' buffers to store temporary data, for which the system is unaware, and the others are operating system interfaces that make the system responsible for the data maintainance from now onwards. You'll distinguish them easily as the former use FILE *
descriptors to operate, while the last use int
integer descriptors to operate on.
So if you use a system call to ensure your data is properly synced to disk, it is absolutely neccessary to first fflush(3)
your process' buffer data before you do the filesystem sync(2)
or fsync(2)
call.
No sync(2)
is warranted to happen at fclose(3)
or even on close(2)
time, or in the atexit()
callbacks your process does before exit()
.
The operating system buffers are write delayed for performance reasons, and close(2)
is not an event that makes it to trigger such a thing. Just think that many processes can be reading and writing the same file at the same time, and each close(2)
triggering a filesystem flush could be a pain to achieve. Operating system triggers such calls at regular intervals, on umount(2)
system calls, on system shutdown, and on specific calls to the sync(2)
and fsync(2)
system calls.
If you need to maintain the FILE *fd
descriptor open, just do a fflush(fd)
for that descriptor to ensure that the operating system has all its buffers for fwrite(3)
d or fprintf(3)
ed data first.
So finally, if you are using <stdio.h>
functions, first do a fflush()
for all the FILE *
descriptors you have written to, or call fflush(NULL);
to tell stdio to synch all descriptors in one call. Then do the sync(2)
or fsync(2)
call to ensure all your data is physically on disk. No need to close anything.
FILE *fd;
...
fflush(fd);
fsync(fileno(fd));
/* here you know that up to the last write(2) or fwrite(3)...
* data is synced to disk */
By the way, your approach of going to /dev/fd/<number>
to get the descriptor (that you had previously) is faulty for two reasons:
Once you close your descriptor, /dev/fd/<number>
is not anymore the descriptor you want. Normally, it doesn't exist, even. Just try this:
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
int main()
{
int fd;
char fn[] = "/dev/fd/1";
close(1); /* close standard output */
fd = open(fn, O_RDONLY); /* try to reopen from /dev/fd */
if (fd < 0) {
fprintf(stderr,
"%s: %s(errno=%d)\n",
fn,
strerror(errno),
errno);
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
} /* main */
You cannot get the directory where an open file belongs to with only the file descriptor. In a multilinked file, there can be thousands of directories just pointing to it. There's nothing on the inode (or in the open file structure) that allows you to get the path used to open that file. A common way to use temporary files is just to create them and immediately unlink(2)
them, so nobody can open it again. As much as you retain the file open you have access to it, but no path points to it anymore.