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.
Related
Somewhere online I've seen a technique to immediately unlink a temporary file after opening it, since you will discard it anyway. As per my understanding of the man-page for unlink, the file will not be unlinked as long as it has an open file descriptor.
When I execute the following piece of code:
char *file, *command;
asprintf(&file, "/tmp/tempXXXXXX");
int fd = mkstemp(file);
unlink(file);
asprintf(&command, "some_command > %s", file); /*Writes n bytes in temp file*/
FILE *f = popen(command, "re");
pclose(f);
struct stat sbuf;
fstat(fd, &sbuf);
printf("%i\n", sbuf.st_size);
close(fd);
free(file);
free(command);
exit(0);
It will print a size of 0. However, if I comment unlink(file), it will show the correct file size. I would expect both scenarios to show the correct size of the file, since unlink should wait till no processes have the file open anymore. What am I missing here?
You're missing the fact that the file referred to by your fd is not the same file as that created by your call to popen().
In a POSIX-like shell, some_command > some_file will create some_file if it does not already exist, otherwise it will truncate some_file.
Your call to popen() invokes a shell, which in turn creates or truncates the output file before invoking some_command as per POSIX.
Since you have unlinked some_file before the call to popen(), the file is created anew: that is, the output file set up by your popen() shell is allocated under a different inode than the (now anonymous) file created by your previous call to mkstemp().
You can see that the files are different if you compare st_ino values from your fstat() (by fd) and a separate call to stat() (by name) after the popen().
I have a function like this which aims to read a file:
int foo(FILE* f)
I want to use flock in order to prevent TOCTTOU. flock requires a file descriptor as an integer. I can get this using fileno(file). The implementation of foo therefore might look like this:
int foo(FILE* f) {
if(!f) return -1;
int fd = fileno(f);
if(fd < 0) return -1;
flock(fd, LOCK_EX);
//do all the reading stuff and so on.
}
However, the evil user might do something like this:
FILE* test;
test = fopen("someexistingfile.txt", "r");
fclose(test);
foo(test);
Then I have a problem because fileno will do invalid reads according to valgrind because it assumes that the file is open.
Any ideas on how to check whether the file is closed?
C11 n1570 7.21.3p4
A file may be disassociated from a controlling stream by closing the file. Output streams are flushed (any unwritten buffer contents are transmitted to the host environment) before the stream is disassociated from the file. The value of a pointer to a FILE object is indeterminate after the associated file is closed (including the standard text streams). Whether a file of zero length (on which no characters have been written by an output stream) actually exists is implementation-defined.
After fclose the use of the value of a FILE * in library functions leads to undefined behaviour. The value of the pointer cannot be used safely for anything at all until reassigned.
In other words, you cannot do really anything at all to discern whether the FILE * value you've given refers to a valid open file or not... well except for testing against NULL - if the value of the pointer is NULL it certainly cannot point to an open stream.
If one can use fopen() then the solution is easy:
FILE *fp;
fp = fopen(path, "r");
fseek (fp, 0, SEEK_END);
size = ftell(fp);
if size is zero the file is empty.
However what if the requirement was that fopen() cannot be used. Instead, what I have to use is system calls like open():
int f = open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR );
ftell() can only be used with FILE type streams. I've googled this and could not find any solutions.
It is not a C question (standard C does not know about open) but a Linux or POSIX one.
You don't necessarily need open(2), and you should realize that on Linux some other process could write into a file that you have opened.
Then you might get the size of a file using stat(2) (with the .st_size field) and you could get the size of an opened file descriptor using fstat
You might also use lseek(2) (it could be used both for setting and querying the current file offset of an opened file descriptor).
I am reading a file in a while loop from start to end:
FILE *file;
file = fopen(path_to_file), "r");
char *line = NULL;
size_t len = 0;
while (getline(&line, &len, file) > 0) {
delete_line_from_file(line);
}
fclose(file);
The function delete_line_from_file() removes the line passed to it from the file. It reads in the whole file via open(fd, O_RDONLY | O_CLOEXEC) + read() + close(), then removes the line from the buffer and writes the whole buffer to the same file via open(fd, O_WRONLY | O_TRUNC | O_CLOEXEC) + write() + close(). The read() is locked in an advisory read-lock via struct flock lk and the write() is locked in an advisory write-lock.
When I read the file there are lines that get missed which has something to do with me reading the file from start to finish in one loop while writing to it. If I read in the whole file and go through the buffer line-by-line no lines get missed. (This is my preferred solution so far.) There are also no mistakes made when truncating and writing the file. The missed lines are still in the file after the loop finishes.
Can I make sure that my while-loop does not miss a line and cleanly empties the file? The file needs to be emptied line-by-line. It cannot be just truncated.
Here is one possible solution I had in mind. Mirror the file via fstat(file &fbuf) and check it's size with if (fbuf.st_size !=0) fseek(file, 0, SEEK_SET); but that seems inefficient.
So is the goal to empty the file completely?
Why don't you open the file as such:
open("file", O_TRUNC | O_WRONLY);
This will open the file with truncation. Alternatively, and perhaps a better solution, you can do this:
fopen("file", "w");
fopen with the "w" option delete the original file and replaces it with the new file of name "file".
Use fseek and ftell inside your loop.
Two processes modifying the same file is a recipe for problems. May be you need to use a pipe(2).
I wish to open a file using the "a+b" mode, i.e. if it does not exist it is created automatically, but if it does I don't want to overwrite it. I want to be able to read and write to the file.
The file is binary, and I want to save records of a specific struct in it. So I want to do fseek() to the record I want and then save the record using fwrite().
The code looks as follows (MyRecord is a typedef to a struct, while FILENAME is a #define to the file's name):
int saveRecord(MyRecord *pRecord, int pos)
{
FILE* file = fopen(FILENAME, "a+b");
if (file == NULL)
{
printf("Unable to open file %s\n", FILENAME);
return 0;
}
fseek(file, pos * sizeof(MyRecord), SEEK_SET);
fwrite(pRecord, sizeof(MyRecord), 1, file);
fclose(file);
return 1;
}
However this code just appends the record to the end of the file, even if I set pos to 0. Why isn't fseek() with SEEK_SET working in append mode?
I know I can simply open it with "r+b" and if it fails open it with "wb", but I want to know why this doesn't work and why fseek() with SEEK_SET is leaving the file pointer at the end. Any references to places where this behaviour is documented appreciated (because I couldn't find any, or I am using the wrong keywords).
That's because in a mode, writing to the FILE* always appends to the end. fseek only sets the read pointer in this mode. This is documented in the C standard, 7.19.5.3 fopen:
Opening a file with append mode ('a' as the first character in the mode argument)
causes all subsequent writes to the file to be forced to the then current end-of-file,
regardless of intervening calls to the fseek function.
Plain C does not have any sane way to achieve what you want. If you're on a POSIX system or anything remotely close, you can use fd=open(FILENAME, O_CREAT|O_RDRW, 0666) and then fdopen(fd, "rb+").
Edit: Another thing you could try, with plain C:
f = fopen(FILENAME, "a+b");
if (!f) /* ... */
tmp = freopen(0, "r+b", f);
if (tmp) f = tmp;
else /* ... */
Use "r+b" mode and fallback to "w+b" if it fails.
The "a+b" mode, allows you to read and append; the "r+b" allows random read and write.
The documentation for fopen describes how the file behaves with the different modes.