Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

File locking using fcntl()

Posted on 2000-02-28
6
Medium Priority
?
679 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Nothing ever in the clear!

This technical paper will help you implement VMware’s VM encryption as well as implement Veeam encryption which together will achieve the nothing ever in the clear goal. If a bad guy steals VMs, backups or traffic they get nothing.

 

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

Industry Leaders: 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!

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…
Have you created a query with information for a calendar? ... and then, abra-cadabra, the calendar is done?! I am going to show you how to make that happen. Visualize your data!  ... really see it To use the code to create a calendar from a q…
Want to learn how to record your desktop screen without having to use an outside camera. Click on this video and learn how to use the cool google extension called "Screencastify"! Step 1: Open a new google tab Step 2: Go to the left hand upper corn…
Suggested Courses

636 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