Wednesday, 3 July 2019

File lock using fcntl()

File lock:


File lock using fcntl()

NAME

       fcntl - manipulate file descriptor

SYNOPSIS

#include <unistd.h>
#include <fcntl.h>

int fcntl(int fd, int cmd, ... /* arg */ );

DESCRIPTION

  • fcntl() performs one of the operations described below on the open file descriptor fd.  The operation is determined by cmd.
  •  fcntl()  can  take  an optional third argument.  Whether or not this argument is required is determined by cmd.  The required argument type is indicated in parentheses after each cmd        name (in most cases, the required type is int, and we identify the argument using the name arg), or void is specified if the argument is not required.


   struct flock {
               ...
               short l_type;    /* Type of lock: F_RDLCK,
                                   F_WRLCK, F_UNLCK */
               short l_whence;  /* How to interpret l_start:
                                   SEEK_SET, SEEK_CUR, SEEK_END */
               off_t l_start;   /* Starting offset for lock */
               off_t l_len;     /* Number of bytes to lock */
               pid_t l_pid;     /* PID of process blocking our lock
                                   (F_GETLK only) */
               ...
           };

  • The l_whence, l_start, and l_len fields of this structure specify the range of bytes we wish to lock.  Bytes past the end of the file may be locked, but not bytes before the start of  the file.
  • F_SETLK (struct flock *)
    • Acquire  a lock (when l_type is F_RDLCK or F_WRLCK) or release a lock (when l_type is F_UNLCK) on the bytes specified by the l_whence, l_start, and l_len fields of lock.  
    • If a conflicting lock is held by another process, this call returns -1 and sets errno to EACCES or EAGAIN.
  • F_SETLKW (struct flock *)
    • As for F_SETLK, but if a conflicting lock is held on the file, then wait for that lock to be released.  If a signal is caught while waiting, then the call is  interrupted  and (after the signal handler has returned) returns immediately (with return value -1 and errno set to EINTR;
    • Note: currently we are using F_SETLKW
  • F_GETLK (struct flock *)
    • On  input  to this call, lock describes a lock we would like to place on the file.  If the lock could be placed, fcntl() does not actually place it, but returns F_UNLCK in the l_type field of lock and leaves the other fields of the structure unchanged. 
    • If one or more incompatible locks would prevent this lock  being  placed,  then  fcntl()  returns details about one of these locks in the l_type, l_whence, l_start, and l_len fields of lock and sets l_pid to be the PID of the process holding that lock.
Note: 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.

RETURN VALUE

    On Success return 0, otherwise retun -1 and errno is set appropriately.

My Understand:

  • Read lock: 
    • Read lock will be blocked if already this file is locked.
    • After unlock it won't reeceive value, but next call it will get
  • Write lock:
    • Write lock will be blocked if already this file is locked with write lock on another process.  
    • After unlock, it exeepcted to overwrite the old value, but reality it won't overwrite the value the output file file contain both value, need to handle this if 2 differect process take the write lock.  

Program:

 
/* File lock using fcntl()
 * Check : http://velrajcoding.blogspot.in
 */
#include <stdio.h>
#include <unistd.h>  // For fcntl
#include <fcntl.h>   // For  struct flock, F_WRLCK & F_SETLKW
#include <errno.h>   // For errno


enum {
    FILE_LOCK,
    FILE_UNLOCK,
};


int file_lock_and_ulock(int fd, const char *path, int is_lock, char ch)
{
    char         str[128] = {0};
    struct flock lock;

    lock.l_type = (is_lock == FILE_LOCK) ? ((ch == 'r') ? F_RDLCK : F_WRLCK) : F_UNLCK;
    lock.l_start = 0;
    lock.l_whence = SEEK_SET;
    lock.l_len = 0;
    lock.l_pid = getpid();

    /*
     * Read lock: read lock will be blocked if already this file is locked.
     *            After unlock it won't reeceive value, but next call it will get
     * Write lock: Write lock will be blocked if already this file is locked with write lock on another process.
     *             After unlock, it exeepcted to overwrite the old value, but reality it won't overwrite the value
     *             the output file file contain both value, need to handle this if 2 differect process take the write lock
     */
    if (fcntl(fd, F_SETLKW, &lock) < 0) {
        strerror_r(errno, str, sizeof(str));
        printf("Error to lock the file errno:%d err des:%s \n", errno, str);
        return 0;
    }
    printf("File %s  is %s \n", path, (is_lock == FILE_LOCK) ? "Locked" : "Unlocked");

    return 1;

}

int file_read()
{
    FILE *fp = NULL;
    int  ret = 0, value = 0;
    unsigned long value_l = 0;
    char str[128] = {0};
    char str_2[128] = {0};


    fp = fopen("test.txt", "r");
    if (!fp) {
        printf("File open is failed errno:%d  ENOENT:<%d>\n", errno, ENOENT);
        return 1;
    }
    ret = file_lock_and_ulock(fileno(fp), "test.txt", FILE_LOCK, 'r');
    if (ret == 0) {
        printf("Error in locking \n");
        return 1;
    }

    fgets(str, sizeof(str), fp);
    fseek(fp, 0, SEEK_SET);
    fscanf(fp, "%s %d", str, &value);
    file_lock_and_ulock(fileno(fp), "test.txt", FILE_UNLOCK, 'r');
    printf("File content on filst line:<%s> & fscan read:%d \n", str, value);

    fseek(fp, 0, SEEK_SET);
    if (fgets(str, sizeof(str), fp) == NULL) {
        printf("Error in retrienve \n");
    }
    memset(str_2, 0, sizeof(str));
    value_l = 0;
    if ((ret = sscanf(str, "%s %lu", str_2, &value_l)) != 2) {
        printf("Error in sscanf: %d  \n", ret);
    }
    printf("File content using fgets & sscanf line:<%s> & fscan read:%d  ret:%d \n", str, value_l, ret);
    return 0;

}

int main()
{
    FILE *fp = NULL;
    int  ret = 0, choose = 0;

    printf("Option for file operation:   \n\t\t 1. Read \n\t\t 2.Write \n Choose your option: ");
    scanf("%d", &choose);

    switch (choose) {
        case 1:
            file_read();
            break;
        case 2:
            fp = fopen("test.txt", "w");
            ret = file_lock_and_ulock(fileno(fp), "test.txt", FILE_LOCK, 'w');
#if defined (AVOID_OVERRIGHT)
            fflush(fp);
            fsync(fileno(fp));
#endif

            if (ret == 0) {
                printf("Error in locking \n");
                return 1;
            }

            //sleep(5);
            fprintf(fp, "file_w_lock_b.c 2nd\n");
            fflush(fp);
            fsync(fileno(fp));
            file_lock_and_ulock(fileno(fp), "test.txt", FILE_UNLOCK, 'w');

            fclose(fp);
            break;
       default:
            printf("Wrong option \n");
    }

    return 0;
}


 

Program 2:

 
/* File lock using fcntl()
 * Check : http://velrajcoding.blogspot.in
 */

#include <stdio.h>
#include <unistd.h>  // For fcntl
#include <fcntl.h>   // For  struct flock, F_WRLCK & F_SETLKW
#include <errno.h>   // For errno

enum {
    FILE_LOCK,
    FILE_UNLOCK,
};


int file_lock_and_ulock(int fd, const char *path, int is_lock)
{
    char         str[128] = {0};
    struct flock lock;

    lock.l_type = (is_lock == FILE_LOCK) ? F_WRLCK : F_UNLCK;
    lock.l_start = 0;
    lock.l_whence = SEEK_SET;
    lock.l_len = 0;
    lock.l_pid = getpid();

    if (fcntl(fd, F_SETLKW, &lock) < 0) {
        strerror_r(errno, str, sizeof(str));
        printf("Error to lock the file errno:%d err des:%s \n", errno, str);
        return 0;
    }
    printf("File %s  is %s \n", path, (is_lock == FILE_LOCK) ? "Locked" : "Unlocked");

    return 1;

}

int file_check_back_slan_n()
{
    FILE     *fp = NULL;
    int      temp = 0;
    char     str[128] = {0};

    if (!(fp = fopen("test_2.txt", "w+"))) {
        printf("Error in open a file \n");
        return 1;
    }

    fprintf(fp, "velraj \n serte %d", 100);
    fflush(fp);
    fsync(fileno(fp));

    fseek(fp, 0, SEEK_SET);
    fscanf(fp, "%d", &temp);

    fseek(fp, 0, SEEK_SET);
    fgets(str, sizeof(str), fp);
    printf("Vel fscanf int:<%d> fgets:<%s> \n", temp, str);
    fclose(fp);

    return 0;
}

int main()
{
    FILE    *fp;
    char         str[128] = {0};

    /*
     *  w      Truncate file to zero length or create text file for writing.  The stream is positioned at the beginning of the file.
     *  w+     Open for reading and writing.  The file is created if it does not exist, otherwise it is truncated.  The stream is positioned at the beginning of the file.
     */
    if ((fp = fopen("test.txt", "w"))) {
        file_lock_and_ulock(fileno(fp), "test.txt", FILE_LOCK);
    } else {
        strerror_r(errno, str, sizeof(str));
        printf("Error to open a file errno:%d err des:%s \n", errno, str);
        return 1;
    }

    fprintf(fp, "file_w_lock.c: 1 This is testing for fprintf...\n");
//    fputs("This is testing for fputs...\n", fp);

#if defined (FLUSH_SYNC_AFTER_SLEEP)
    printf("Going to sleep and flush flush & sync after sleep");
    sleep(10);
#endif
    fflush(fp);
    fsync(fileno(fp));
#if !defined (FLUSH_SYNC_AFTER_SLEEP)
    printf("flush & sync donw now gonig to sleep ");
    /* After flush & sync the file */
    sleep(10);
#endif
    file_lock_and_ulock(fileno(fp), "test.txt", FILE_UNLOCK);
    fclose(fp);

    return 0;
    if ((fp = fopen("test.txt", "w"))) {
        file_lock_and_ulock(fileno(fp), "test.txt", FILE_LOCK);
    } else {
        strerror_r(errno, str, sizeof(str));
        printf("Error to open a file errno:%d err des:%s \n", errno, str);
        return 1;
    }



    fflush(fp);
    fsync(fileno(fp));
    file_lock_and_ulock(fileno(fp), "test.txt", FILE_UNLOCK);
    fclose(fp);

    file_check_back_slan_n();

    return 0;
} 

Output: 

   Lock & lock from first terminal 1st terminal:
           abuser@labuser-virtual-machine:~/velrajk/sample$ ./a.out
           File test.txt  is Locked
           File test.txt  is Unlocked
           labuser@labuser-virtual-machine:~/velrajk/sample$
           labuser@labuser-virtual-machine:~/velrajk/sample$



   Not using lock on second file 2nd terminal:
           abuser@labuser-virtual-machine:~/velrajk/sample$
           labuser@labuser-virtual-machine:~/velrajk/sample$
           labuser@labuser-virtual-machine:~/velrajk/sample$ cat test.txt
           from_2
           labuser@labuser-virtual-machine:~/velrajk/sample$ === now antoher progr is still inlocking  ======
           ===: command not found
           labuser@labuser-virtual-machine:~/velrajk/sample$
           labuser@labuser-virtual-machine:~/velrajk/sample$
           labuser@labuser-virtual-machine:~/velrajk/sample$ ==== that is over unlcoked here not used lock ====
           ====: command not found
           labuser@labuser-virtual-machine:~/velrajk/sample$ cat test.txt
           This is testing for fprintf...
           This is testing for fputs...
           labuser@labuser-virtual-machine:~/velrajk/sample$

  issue:
  ------
   Lock on process 1 process 2 is got hanged due to process 1 lock, after process 1 release, process 2 write on first still process 1 data also present.


   labuser@labuser-virtual-machine:~/velrajk/sample$ cat test.txt
   from_2
   testing for fprintf...
   This is testing for fputs...
   labuser@labuser-virtual-machine:~/velrajk/sample$

No comments:

Post a Comment