20

I wonder if there is any way to lock and unlock a file in Linux when I open a file using fopen (not open)?

Based on Stack Overflow question C fopen vs open, fopen is preferred over open.

How can I implement my own file lock (if possible) by creating and deleting lock files?

Community
  • 1
  • 1
Amir
  • 5,136
  • 11
  • 40
  • 56

7 Answers7

19

I would strongly disagree with the claim that fopen is prefered over open. It's impossible to use fopen safely when writing a file in a directory that's writable by other users due to symlink vulnerabilities/race conditions, since there is no O_EXCL option. If you need to use stdio on POSIX systems, it's best to use open and fdopen rather than calling fopen directly.

Now, as for locking it depends on what you want to do. POSIX does not have mandatory locking like Windows, but if you just want to ensure you're working with a new file and not clobbering an existing file or following a symlink, use the O_EXCL and O_NOFOLLOW options, as appropriate. If you want to do cooperative locking beyond the initial open, use fcntl locks.

R.. GitHub STOP HELPING ICE
  • 195,354
  • 31
  • 331
  • 669
  • 1
    I can't make a 1 character edit, would you mind correcting that typo, it's a bit confusing near `... rather than calling fdopen directly ...` -- I am assuming you meant `fopen` – MickLH May 22 '15 at 17:37
18

In Linux, if you need a file descriptor (e.g., to pass to a file-locking primitive), you can use fileno(FILE*) to retrieve it. After retrieving the file descriptor, you can use it as if it had been returned by open.

For example, instead of

int fd = open("myfile.txt", flags);
int result = flock(fd, LOCK_SH);

you could equally well do this:

FILE* f = fopen("myfile.txt", "r");
int result = flock(fileno(f)), LOCK_SH);

Note that fileno is defined in the POSIX standard, but not in C or C++ standards.

As for your second question, the Linux open() man page has this to say:

The solution for performing atomic file locking using a lockfile is to create a unique file on the same file system (e.g., incorporating hostname and pid), use link(2) to make a link to the lockfile. If link() returns 0, the lock is successful. Otherwise, use stat(2) on the unique file to check if its link count has increased to 2, in which case the lock is also successful.

Robᵩ
  • 143,876
  • 16
  • 205
  • 276
  • 3
    @EdHeal: Streams have buffers, which just means you'll have to be careful to flush at appropriate times. – derobert Sep 27 '11 at 17:42
  • @derobert - flushing is ok for writing. But consider reading part of a file, some of it is in the buffer, some of it not in the buffer – Ed Heal Sep 27 '11 at 17:49
  • @EdHeal: Well, obviously, one must flush the input buffer as well. Not hard, considering `fflush` does so... – derobert Sep 27 '11 at 17:53
  • @derobert - i.e. so the streams mechanism adds extra overhead. Better to use `read` and `write` system calls in the first place. – Ed Heal Sep 27 '11 at 17:54
  • @EdHeal: Depends on what you're doing. If you're going to obtain a lock, do a lot of IO, then release the lock, the buffer flushing isn't so bad. But yeah, if you're constantly flushing buffers, then you should use one of the better methods (read/write or mmap). – derobert Sep 27 '11 at 17:57
5

Files can be locked by using flock(). Its syntax is

 #include <sys/file.h>
 #define   LOCK_SH   1    /* shared lock */
 #define   LOCK_EX   2    /* exclusive lock */
 #define   LOCK_NB   4    /* don't block when locking */
 #define   LOCK_UN   8    /* unlock */

int flock(int fd, int operation);

First file is opened using fopen() or open(). Then this opened file is locked using flock() as given below

int fd = open("test.txt","r");
int lock = flock(fd, LOCK_SH);  // Lock the file . . .
// . . . .
// Locked file in use 
// . . . .
int release = flock(fd, LOCK_UN);  // Unlock the file . . .
Yogeesh H T
  • 2,093
  • 18
  • 17
  • 3
    `open()` does not accept a string argument for mode. That's `fopen()`. But then again, fopen does not return an int, but rather a FILE* . Please edit to clarify exactly what you meant to use – kyriakosSt Apr 03 '19 at 14:22
2

Note that in below code fopen will fail (and return NULL) if the lock file /var/lock/my.lock doesn't exist.

FILE* f = fopen("/var/lock/my.lock", "r");
int result = flock(fileno(f)), LOCK_SH);

Use fopen with w+ if you need the lockfile to be created if it doesn't exist.

FILE* f = fopen("/var/lock/my.lock", "w+");
int result = flock(fileno(f)), LOCK_SH);
Shoaib Ahmed
  • 415
  • 2
  • 7
1

If you wish to implement your own lock simply, I suggest Rob's answer of using flock. If you wish to implement it in a complex manner, such as for high availability, you can try something like using a thread to touch a file at a regular interval. All other programs wanting to lock the file should also check the file to see if its update time has been updated in at another fixed, but larger interval (the larger part is important). This is probably overkill for most applications, but it handles things such as crashes, freezes, etc. much better than flock.

dbeer
  • 6,233
  • 3
  • 27
  • 46
0

There is another way with open() function, but I'm not sure about this called locked file. I am using file permissions to open a file.

The code is here:

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

#define FILE_NAME "hello.txt"

int main()
{
    int fd;

    fd = open(FILE_NAME, O_CREAT, S_IRWXU);

    // Error checking
    if(fd == -1){
        perror("[error]\n");
    }
    else{
        printf("[file is opened]\n");
    }

    return 0;
}

I used a flag for permissions (third argument). This flag gives read, write and execute permissions to the user.

$ls -alh

total 24K
drwxrwxr-x  2 arien arien 4.0K Dec 28 20:56 .
drwxrwxr-x 18 arien arien 4.0K Dec 27 22:20 ..
-rwxrwxr-x  1 arien arien 8.5K Dec 28 20:56 fopen
-rw-rw-r--  1 arien arien  290 Dec 28 20:56 fopen.c
-rwx------  1 arien arien    0 Dec 28 20:55 hello.txt

A little tip: If you are using Ubuntu or Debian, you can see the description of functions with man [function_name] man pages of open() function.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
metarose
  • 125
  • 4
  • 16
0

The below code is not letting me lock the file using lockf, flock works fine though

    #include <iostream>
    #include <unistd.h>
    #include<thread>
    #include <vector>
    #include <sys/file.h>
    #include <fcntl.h>
    #include <string.h>

using namespace std;

void append()
{
    FILE *fp=fopen("a.txt","a");
    if(fp)
    {
        cout<<lockf(fileno(fp),F_LOCK,0)<<endl;

        //flock(fileno(fp), LOCK_EX);

        fprintf(fp,"abcdefghijklmnopqrstuvwxyz\n");fflush(fp);
        sleep(1);
        fprintf(fp,"^$^&%&*&^&*(*)_*)_()_*&***&(\n");fflush(fp);

        fclose(fp);
    }
    else {
        printf("null\n");
    }
}


int main()
{
    fclose(fopen("a.txt","w"));
    //return 0;
    vector<thread*> v;
    //#pragma omp parallel for
    for(int i=0;i<1000;++i)
    {
        v.push_back(new thread(append));

        //append();
    }

    for(auto y:v)
    {
        y->join();
        delete y;
    }

    return 0;
}