-2

I was trying to write a test program to test if file locking works correctly. But, one of the tests is getting failed. I am trying to acquire a read lock on a file, but the test failed. When I tried to print the error number, it printed 9 which indicated EBADF i.e. bad file number (or file descriptor). I also tried to print file descriptor value, it printed 3 which seems to be a valid file descriptor. I am using Netbeans IDE and its Simple Test functionality.

The file to be read is in the same folder as the source code file.

Here's my code:

#include <stdlib.h>
#include <iostream>
#include <sys/file.h>
#include <sys/unistd.h>
using namespace std;
/*
 * Simple C++ Test Suite
 */
int acquireRLock(int fd, struct flock &lock) {
    lock.l_len = 0;
    lock.l_start = 0;
    lock.l_whence = SEEK_SET;
    lock.l_type = F_RDLCK;
    struct flock temp_lock = lock;
    fcntl(fd, F_GETLK, &temp_lock);
    if(temp_lock.l_type == F_WRLCK) {
        return -1;
    }
    else {
        int ret_val = fcntl(fd, F_SETLK, &lock);
        if(ret_val == -1) cout << "Failed here errno = " << errno << " fd = " << fd << endl;
        return ret_val;
    }
}
int acquireWLock(int fd, struct flock &lock) {
    lock.l_len = 0;
    lock.l_start = 0;
    lock.l_whence = SEEK_SET;
    lock.l_type = F_WRLCK;
    struct flock temp_lock = lock;
    fcntl(fd, F_GETLK, &temp_lock);
    if(temp_lock.l_type == F_RDLCK || temp_lock.l_type == F_WRLCK)
        return -1;
    else
        return fcntl(fd, F_SETLK, &lock);
}
int FileOpenTest() {
    int fd = open("test_file.txt", O_WRONLY);
    if(fd == -1) {
        cout << "%TEST_FAILED% time=0 testname=FileOPenTest (newsimpletest) message=file open failed" << endl;
    }
    return fd;
}
void FileReadLockTest(int fd) {
    struct flock lock;
    if(acquireRLock(fd, lock) < 0) {
        cout << "%TEST_FAILED% time=0 testname=FileReadLockTest (newsimpletest) message=file read lock acquire failed" << endl;
    }
}
void FileWriteLockTest(int fd) {
    struct flock lock;
    if(acquireWLock(fd, lock) < 0) {
        cout << "%TEST_FAILED% time=0 testname=FileWriteLockTest (newsimpletest) message=file write lock acquire failed" << endl;
    }
}
void FileWriteTest(int fd) {
    if(write(fd, (void*) "I am a ball", 13) <= 0) {
        cout << "%TEST_FAILED% time=0 testname=FileWriteTest (newsimpletest) message=file write failed" << endl;
    }
}
void FileCloseTest(int fd) {
    if(close(fd) < 0) {
        cout << "%TEST_FAILED% time=0 testname=FileCloseTest (newsimpletest) message=file close failed" << endl;
    }
}
int main(int argc, char** argv) {
    std::cout << "%SUITE_STARTING% newsimpletest" << std::endl;
    std::cout << "%SUITE_STARTED%" << std::endl;

    std::cout << "%TEST_STARTED% FileOpenTest (newsimpletest)" << std::endl;
    int fd = FileOpenTest();
    std::cout << "%TEST_FINISHED% time=0 FileOpenTest (newsimpletest)" << std::endl;

    std::cout << "%TEST_STARTED% FileReadLockTest (newsimpletest)\n" << std::endl;
    FileReadLockTest(fd);
    std::cout << "%TEST_FINISHED% time=0 FileReadLockTest (newsimpletest)" << std::endl;

//    std::cout << "%TEST_STARTED% FileWriteLockTest (newsimpletest)\n" << std::endl;
//    FileWriteLockTest(fd);
//    std::cout << "%TEST_FINISHED% time=0 FileWriteLockTest (newsimpletest)" << std::endl;
//    
//    std::cout << "%TEST_STARTED% FileWriteTest (newsimpletest)\n" << std::endl;
//    FileWriteTest(fd);
//    std::cout << "%TEST_FINISHED% time=0 FileWriteTest (newsimpletest)" << std::endl;

    std::cout << "%TEST_STARTED% FileCloseTest (newsimpletest)\n" << std::endl;
    FileCloseTest(fd);
    std::cout << "%TEST_FINISHED% time=0 FileCloseTest (newsimpletest)" << std::endl;

    std::cout << "%SUITE_FINISHED% time=0" << std::endl;

    return (EXIT_SUCCESS);
}

It shows following output:

Output

On un-commenting the code to acquire write lock, write lock is acquired and file is written, and the read lock still fails as shown in the output below. But, the actual behavior should be success for read lock and failure for write lock:

enter image description here

Please help me in finding out why the test is failing.

kiner_shah
  • 3,939
  • 7
  • 23
  • 37

1 Answers1

2

On the man page for fcntl we can read

In order to place a read lock, fd must be open for reading. In order to place a write lock, fd must be open for writing. To place both types of lock, open a file read-write.

You are opening the file with

int fd = open("test_file.txt", O_WRONLY);

From man page for open

O_RDONLY Open for reading only.

O_WRONLY Open for writing only.

O_RDWR Open for reading and writing. The result is undefined if this flag is applied to a FIFO.

For placing read and write locks, open the file with

int fd = open("test_file.txt", O_RDWR);
super
  • 12,335
  • 2
  • 19
  • 29
  • Yes, now the read lock test is passing! But, now I am facing a different problem; the write lock is not failing as expected. If on same file read lock is there, write lock shouldn't be successful. The lock type (`temp_lock`) returned is `3` which is weird. – kiner_shah Jun 30 '18 at 14:12
  • 1
    This is also specified in the links in my answer. A single process can hold only one type of lock on a file region; if a new lock is applied to an already-locked region, then the existing lock is converted to the new lock type. – super Jun 30 '18 at 14:25
  • Ohh yeah, I forgot about that. Thanks for mentioning! :-) – kiner_shah Jun 30 '18 at 14:26
  • I'm having a similar issue. Is it really not possible to use O_RDONLY with open and then set an exclusive lock using fcntl with a l_type of F_WRLCK? How does one open a file for reading whilst preventing anything else from reading/writing the file? – Dave Nottage Apr 03 '19 at 06:50
  • 1
    @DaveNottage, first of all sorry for late reply, I don't know how I missed the notification for your comment. Secondly, no, it isn't possible to open file with O_RDONLY and try a write lock on it. You will need O_RDWR for that. Thirdly, to open a file for reading and avoid anyone else to read/write in the file, check for the current lock and throw an error if current lock is F_RDLCK. Do this, for example, in `acquireRLock()` and `acquireWLock()` in my code. – kiner_shah Aug 29 '19 at 13:35