?
Solved

File locking using fcntl()

Posted on 2000-02-28
6
Medium Priority
?
690 Views
Last Modified: 2008-03-03
I am trying to use the posix fcntl() to lock a file for read/write or unlock it. For some reason, it doesn't work...
Here's the code. Appreciate your advice.
#include <iostream.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
/*****************************************************
struct flock
{
    short l_type ;  /* F_RDLCK, F_WRLCK, F_UNLCK */
    short l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
    off_t  l_start;  /* relative starting offset in bytes */
    off_t  l_len;   /* #bytes; 0 means until EOF */
    pid_t l_pid; /* PID returned by F_GETLK *?
};
*****************************************************/
#define MY_NO_LOCK (F_UNLCK)
#define MY_READ_LOCK (F_RDLCK)
#define MY_WRITE_LOCK (F_RDLCK | F_WRLCK)

int lockFile(int fd, int lockType)
{
    struct flock fileLock;

    fileLock.l_type = 0;
    fileLock.l_whence = SEEK_SET;
    fileLock.l_start = 0;
    fileLock.l_len = 0;  /*  lock entire file */
    fileLock.l_pid = 0;

    if( fcntl(fd, F_GETLK, &fileLock) < 0 )
        return (-1); /* error */

    cout << "fcntl() returned lock type " << fileLock.l_type << endl;

    if( fileLock.l_type == MY_NO_LOCK )
    {
        cout << "File not currently locked" << endl;
        fileLock.l_type = lockType;
        fileLock.l_pid = 0;
        return fcntl( fd, F_SETLKW, &fileLock );
    }
    if( (fileLock.l_type == MY_READ_LOCK) && (lockType == MY_READ_LOCK) )
    {
        cout << "File is already READ locked by process " << fileLock.l_pid << endl;
        cout << "Current thread will increment lock count" << endl;
        fileLock.l_pid = 0;
        fileLock.l_type = lockType;
        return fcntl( fd, F_SETLKW, &fileLock );
    }
    if( (lockType == MY_WRITE_LOCK) )
    {
        cout << "File is already WRITE locked by process " << fileLock.l_pid << endl;
        cout << "Current thread will block for the lock " << endl;
        fileLock.l_pid = 0;
        fileLock.l_type = lockType;
        return fcntl( fd, F_SETLKW, &fileLock );
    }
}

char getLockTypeFromUser()
{
    char ch = 'X';
    while( ch != 'R' && ch != 'r' && ch != 'W' && ch != 'w'  && ch != 'U' && ch != 'u' && ch != 'Q' && ch != 'q' )
    {
        cout    << "     R for read lock " << endl
                << "     W for write lock " << endl
                << "     U for unlock " << endl
                << "     Q for unlock and quit " << endl
                << " : " ;
        ch = getchar();
    }
    cout << "Thanks !" << endl;
    getchar(); /* remove \r from stdin */

    return ch;
}
int main ()
{
    cout << "Current process id is " << getpid() << endl;
    int fd = open( "/tmp/testfile", O_RDWR );
    if ( fd < 0 )
    {
        cout << "Unable to open file : error " << errno << endl;
        return -1;
    }

    while (1)
    {
        switch( getLockTypeFromUser() )
        {
        case 'w':
        case 'W' :
            if( lockFile(fd, MY_WRITE_LOCK) < 0 )
                cout << "Unable to lock file : error " << errno << endl;
            else
                cout << "File locked for WRITE" << endl;
        break;
        case 'r':
        case 'R' :
            if( lockFile(fd, MY_READ_LOCK) < 0 )
                cout << "Unable to lock file : error " << errno << endl;
            else
                cout << "File locked for READ" << endl;
        break;
        case 'u':
        case 'U' :
            if( lockFile(fd, MY_NO_LOCK) < 0 )
                cout << "Unable to unlock file : error " << errno << endl;
            else
                cout << "File unlocked" << endl;
        break;
        case 'q':
        case 'Q' :
            if( lockFile(fd, MY_NO_LOCK) < 0 )
                cout << "Unable to unlock file : error " << errno << endl;
            else
                cout << "File unlocked ... exiting." << endl;
            close( fd );
            return 0;
        break;
        }
    }
}


0
Comment
Question by:Marthi
  • 3
  • 3
6 Comments
 
LVL 40

Accepted Solution

by:
jlevie earned 300 total points
ID: 2585237
Unfortunately the Linux man page for fcntl (at least on Redhat 6.1) doesn't fully describe the use of F_SETLK & friends. I had to look at the man page for fcntl on Solaris to figure it out.

I've included a simple pair of programs that demonstrates the use. The "Locker" needs to be running before "Writer" is executed. Since I don't have much use for C++, I wrote the pair in C.

--- Locker Starts here ---
/*
 * Playtoy for file locks via fnctl
 * Locker creates and locks a file (/tmp/testfile)
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

extern int errno;

main()
{
  int fd, c;
  struct flock lock;

  if((fd = open("/tmp/testfile", O_RDWR|O_CREAT))==0)
  {
    perror("Locker - Failed to create testfile");
    exit(1);
  }
  lock.l_type = F_WRLCK;
  lock.l_whence = SEEK_SET;     /* Set locked area to begin ... */
  lock.l_start = 0;             /* ... zero bytes from start of file ... */
  lock.l_len = 0;               /* ... zero means to end of file */
  if(fcntl(fd, F_SETLK, &lock)==-1)
  {
    perror("Locker - Failed to set lock");
    exit(1);
  }
  printf("Writer - Lock succeeded, hit return to exit\n");
  c = getchar();
}

--- Writer Starts here ---
/*
 * Playtoy for file locks via fnctl
 * Writer attempts to open for writing a locked file (/tmp/testfile)
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

extern int errno;

main()
{
  int fd, c;
  struct flock lock;

  if((fd = open("/tmp/testfile", O_RDWR))==0)
  {
    perror("Writer - Failed to open file");
    exit(1);
  }
  lock.l_type = F_WRLCK;
  lock.l_whence = SEEK_SET;
  lock.l_start = 0;
  lock.l_len = 0;
  if(fcntl(fd, F_SETLK, &lock)==-1)
  {
    if(fcntl(fd, F_GETLK, &lock)==-1)
    {
      perror("Writer - Failed to retrieve lock struct");
    }
    perror("Writer - Can't get lock");
    printf("Locked by process: %ld\n", lock.l_pid);
    exit(1);
  }
  printf("Writer - Lock succeeded, hit return to exit\n");
  c = getchar();
}
0
 

Author Comment

by:Marthi
ID: 2588705
It worked for me on Linux too... Thanks !
0
 
LVL 40

Expert Comment

by:jlevie
ID: 2588942
Was the code enough of an explanation as to how it works? After reading Sun's man page (lots & lots more info compared to Linux), I wasn't sure if the code alone would suffice.
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:Marthi
ID: 2589386
Your code helped me find the right way to use fcntl(F_SETLK) and fcntl(F_GETLK). Combined with the information in Linux header files and W.R.Stevens, I could get my file locks working.

So, Thanks again...

This was one of the things I was experimenting with and was having trouble. However, I probably need a different solution altogether. If you are interested, please read on.

I have a small persistent database on the filesystem. Each "table" of this "database" is a file. I need to read and write lock the file so that other processes as well as threads within the same process are synchronized in their access to the files.

To do this, (with your kind help) I put together a small scheme of locking the process and then locking the database file (both using fcntl()). Although the database is small, it can easily have upwards of 700 such files. For this application fcntl() is sub-optimal and something like a POSIX - process shared mutex would be nicer. Unfortunately, those are not implemented on Linux (apparently, they are on Solaris, AIX ??)

Still looking for an optimal solution.

Kailash
0
 
LVL 40

Expert Comment

by:jlevie
ID: 2589967
Other than using an actual database, which may not be possible depending on the nature of the data, you're right. What you need is a  process shared mutex facility. I suspect we'll get them eventually in Linux as the SMP kernel work progresses.

Using fcntl to implement advisory locks works okay between processes, but I don't think it's a good solution for a multi-threaded process. The only other way would be to change the design and have all I/O to/from the files controlled by one process and use Unix sockets for intertask I/O. Not elegant, but you can absolutely control who gets what access and when.
0
 

Author Comment

by:Marthi
ID: 2594159
Thanks for your attention.

My "application" is really a library that other processes link with. You can think of it as the registry in Windows NT/95/98, except it holds data differently and so on.

While controlling I/O through one process is feasible, I don't think I'll be allowed to do that. I was thinking of a distributed lock manager process which will use pthread_mutex_t 's to control access and every process (thru' my API) will attempt to get a read/write lock from the lock manager before accessing the file.

My solution is no more elegant than yours and the real problem is getting approval for the changed design... :-(

Thanks all the same. I've enjoyed discussing this with you.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The purpose of this article is to fix the unknown display problem in Linux Mint operating system. After installing the OS if you see Display monitor is not recognized then we can install "MESA" utilities to fix this problem or we can install additio…
The purpose of this article is to demonstrate how we can upgrade Python from version 2.7.6 to Python 2.7.10 on the Linux Mint operating system. I am using an Oracle Virtual Box where I have installed Linux Mint operating system version 17.2. Once yo…
Loops Section Overview
Is your OST file inaccessible, Need to transfer OST file from one computer to another? Want to convert OST file to PST? If the answer to any of the above question is yes, then look no further. With the help of Stellar OST to PST Converter, you can e…

807 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question