4

Background

In my C program, I am reading from a file line by line.

FILE *file = fopen("config.txt", "r");

if (file)
{
    char *line;
    size_t length;
    size_t read;
    int test_case_number = 0;

    while ((read = getline(&line, &length, file)) != -1)
    {
        printf("%s", line);
    }
}
else
{
    fputs("The provided <PATH_TO_CONFIG FILE> does not exist.\n\n");
    exit(1);
}

Issue

However, while I am reading this file, I want to prevent any other process from writing to config.txt while its being read. How can I do this?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
Nicholas Adamou
  • 711
  • 10
  • 23
  • 1
    Check out the man page for `flock()`. – LSerni Dec 08 '19 at 17:17
  • @LSerni I have, but I am not sure if `flock()` will work with `fopen()` – Nicholas Adamou Dec 08 '19 at 17:18
  • You want *mandatory* file locking then. That depends on a lot of things. What is your platform? – LSerni Dec 08 '19 at 17:28
  • 1
    Standard C doesn't provide a mechanism for preventing concurrent readers and writers. POSIX provides voluntary mechanisms such as [`fcntl()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html) and [`lockf()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lockf.html) but if the writing process doesn't test for a lock, there's not much the reader can do. Some systems provide mandatory locking with a special `chmod()` mode on a file but many do not. – Jonathan Leffler Dec 08 '19 at 17:29
  • @LSerni Ubuntu Linux – Nicholas Adamou Dec 08 '19 at 17:29
  • Some POSIX-like systems use the IS_SGID bit on non-executable files to indicate mandatory file locking. That was provided by SVID (System V Interface Definition). POSIX does not mandate mandatory locking — it does mention it in the rationale for [`chmod()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html). Also note that POSIX does not standardize `flock()` — it has `lockf()` and `fcntl()` which is quite enough. Nevertheless, `flock()` is widely available. – Jonathan Leffler Dec 08 '19 at 17:57

2 Answers2

1

In Linux, you can use flock() (focus by me):

Apply or remove an advisory lock on an open file.

LOCK_EX Place an exclusive lock. Only one process may hold an exclusive lock for a given file at a given time.

However, you'd need to open your file with open(), instead of fopen().

Question with abstract example: flock(): removing locked file without race condition? Or check this example.

IMPORTANT: As @JonathanLeffler commented "Note the term "advisory lock" — that means that if the writing process does not test for the lock, it will be able to write. On POSIX-like systems, you can use flock() or lockf() or fcntl() locking on the file descriptor via fileno(fp)".

Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 1
    Note the term "advisory lock" — that means that if the writing process does not test for the lock, it will be able to write. On POSIX-like systems, you can use `flock()` or `lockf()` or `fcntl()` locking on the file descriptor via `fileno(fp)`. – Jonathan Leffler Dec 08 '19 at 17:30
  • If I `open()` then `flock()`, how would I implement the reading functionality that I have since I don't have `getLine()`? – Nicholas Adamou Dec 08 '19 at 17:31
  • @NicholasAdamou: if you use file descriptors (`open()`), then you use `read()` and `write()` as your primary I/O interfaces (and `close()` to finish up), though there are a plethora of alternatives for various special I/O purposes. – Jonathan Leffler Dec 08 '19 at 17:32
  • @NicholasAdamou that's the easy part, there are a plethora of examples out there doing that. Notice what Jonathan mentioned in his first comment though, it's very important. – gsamaras Dec 08 '19 at 17:34
1

If you have to "defend" against your own processes or "well behaved" processes, then you want to use flock:

   fp = fopen(fileName, fileMode);
   if (fp != NULL) {
       flock(fileno(fp), LOCK_EX);
       ...
       fclose(fp); fp = NULL;
   }

But those locks are advisory only, that is, any other process can choose to ignore them. Or not bother checking. I've just tried creating a file, opening it and locking with LOCK_EX, then sleeping for 60 seconds. During that time, another process was free to do whatever it wanted to the file (Linux, Ubuntu 18.04-LTS).

But if you need mandatory locking, that's not something that is available on all platforms since the kernel has to cooperate. Here you will find a description and example on how the limited mandatory lock support in Linux can be used.

On the other hand, in Windows this is automatic:

Windows defaults to automatic, mandatory file locking. UNIXes default to manual, cooperative file locking. In both cases, the defaults can be overridden, but in both cases they usually aren’t. (source).

A workaround can be to temporarily rename the file being worked on to a unique name, possibly creating an empty file with the old name in its place, then renaming it back once you've done; or copying the file as above, leaving a copy for other programs to read. Write denial can also be achieved by changing the file permissions. These solutions require to handle some edge cases, e.g. where your process crashes or the machine hangs before setting things back as they were.

LSerni
  • 55,617
  • 10
  • 65
  • 107