Solved

File locking using fcntl()

Posted on 2000-02-28
6
618 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 100 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
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 

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

Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

Join & Write a Comment

Have you ever been frustrated by having to click seven times in order to retrieve a small bit of information from the web, always the same seven clicks, scrolling down and down until you reach your target? When you know the benefits of the command l…
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…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…
This tutorial demonstrates a quick way of adding group price to multiple Magento products.

760 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

Need Help in Real-Time?

Connect with top rated Experts

24 Experts available now in Live!

Get 1:1 Help Now