Introduction to the fcntl() function

The fcntl() function can be used to change the properties of a file that is already open. The fcntl() funciton is extremely versatile, and we will be looking at it more than once. The fcntl() function can be used for five different purposes, each purpose defined by an integer constant set as its second argument. The five purposes are one, duplicating an existing descriptor; two, getting or setting file descriptor flags; three, getting or setting file status flags; four, getting or setting asynchronous I/O ownership; and, five, getting or setting record locks.

#include <stdio.h>
#include <fcntl.h>

int main(void){
    
    printf("duplicate an existing file descriptor (F_DUPFD) %d\n", F_DUPFD);
    printf("get file descriptor flags (F_GETFD) %d\n", F_GETFD);
    printf("set file descriptor flags (F_SETFD) %d\n", F_SETFD);
    printf("get file status flags (F_GETFL) %d\n", F_GETFL);
    printf("set file status flags (F_SETFL) %d\n", F_SETFL);
    printf("get record locks (F_GETLK) %d\n", F_GETLK);
    printf("set record locks (F_SETLK) %d\n", F_SETLK);
    printf("get asynchronous I/O ownership (F_GETOWN) %d\n", F_GETOWN);
    printf("set asynchronous I/O ownership (F_SETOWN) %d\n", F_SETOWN);
    
    return 0;
    
}

Note that the fcntl() function is defined in the <fcntl.h> header file.

The F_DUPFD command value duplicates the file descriptor that is the first argument to the fcntl() function. The new file descriptor is the return value of the function; as with open(), it is the lowest numbered descriptor that is not open. However, we can also specify a third numeric argument to fcntl() that defines the floor value for assigning to a file descriptor, so that the file descriptor value will be no less than the number specified.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>

int main(void){
    
    int fdOne, fdTwo, fdThree;
    
    fdOne = open("misc.txt", O_CREAT | O_TRUNC, S_IRWXU);
    
    if(fdOne < 0){
        printf("Error opening / creating test.txt. ");
        if(errno==EACCES){
            printf("Error #%d (EACCES): Permission denied.\n", EACCES);
            exit(EXIT_FAILURE);
        }
    } else {
        printf("test.txt created / opened ");
        printf("with file descriptor %d.\n", fdOne);
    }
    
    //lets use the fcntl() function to copy the 
    //file descriptor
   if((fdTwo = fcntl(fdOne, F_DUPFD, 0))<0){
       printf("Error duplicating file descriptor.\n");
       exit(EXIT_FAILURE);
   } else {
       printf("File descriptor duplicated. ");
       printf("New file descriptor is %d.\n", fdTwo);
   }
    
   //set the file descriptor to be a higher number;
    
    if((fdThree = fcntl(fdOne, F_DUPFD, 11))<0){
        printf("Error duplicating file descriptor.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File descriptor duplicated. ");
        printf("New file descriptor is %d.\n", fdThree);
    }
    
    close(fdOne);
    close(fdTwo);
    close(fdThree);
    
    return 0;
    
}

Note that close() is defined in the <unistd.h> header file.

The F_GETFL command specifier returns the file status flags as the value of the function. These are the same file status flags used with the open() function. The O_RDONLY flag sets the file to open for reading only. The O_WRONLY flag sets the file to be open for writing only. The O_RDWR flag sets the file to be open for reading and writing.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>


int main(void){
    
    int fflags, rVal, fd;
    
    if((fd = open("afile", O_CREAT | O_APPEND, S_IRWXU))<0){
        printf("error opening file.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File opened.\n");
    }
    
    if((rVal = fcntl(fd, F_GETFL))<0){
        printf("Error getting file status flags.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File status flags retrieved.\n");
    }
    
    //O_ACCMODE has the value of three
    fflags = rVal & O_ACCMODE;
    if(fflags == O_RDONLY){
        printf("File is read only.\n");
    } else if (fflags == O_WRONLY){
        printf("File is write only.\n");
    } else if (fflags == O_RDWR){
        printf("File is read / write.\n");
    } else {
        printf("No idea.\n");
    }
    
    
    if(close(fd)<0){
        printf("Error closing file.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File descriptor closed.\n");
    }
    
    
    return 0;
}

The O_APPEND mode causes all write actions to happen at the end of the file. In this mode, the file pointer is moved to the end of the file before writing, disregarding wherever the file pointer was at before the call to write(). The O_NONBLOCK mode causes the file to open in non-blocking mode. In non-blocking mode, no operations on the file descriptor will cause the calling process to wait.

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>

void readFlags(int fd){
    int returnValue;
    if((returnValue = fcntl(fd, F_GETFL, 0))<0){
        printf("Error opening file descriptor %d.\n", fd);
        return;
    }
    printf("Access mode flags for fd %d: ", fd);
    switch(returnValue & O_ACCMODE){
        case O_RDONLY:
            printf("Read only. ");
            break;
        case O_WRONLY:
            printf("Write only. ");
            break;
        case O_RDWR:
            printf("Read and write. ");
            break;
        default:
            printf("Unknown access mode. ");
            break;
    } // end switch
    if(returnValue & O_APPEND){
        printf("+Append.");
    }
    if(returnValue & O_NONBLOCK){
        printf("+Nonblocking");
    }
    printf("\n");
} // end function readFlags ---------------

int main(void){
    
    int fdOne, fdTwo;
    
    if((fdOne = open("append.txt", O_CREAT | O_APPEND, S_IRWXU))<0){
        printf("Error opening append.txt.\n");
        exit(EXIT_FAILURE);
    } else {
        printf("File opened with fd %d.\n", fdOne);
    }
    
    
    if((fdTwo = open("nonblocking.txt", O_CREAT | O_NONBLOCK, S_IRWXU))<0){
       printf("Error opening nonblocking.txt");
       exit(EXIT_FAILURE);
    } else {
        printf("File opened with fd %d.\n", fdTwo);
    }
    
    readFlags(fdOne);
    readFlags(fdTwo);
    
    if((close(fdOne))<0){
        printf("Error closing fdOne.\n");
        exit(EXIT_FAILURE);
    }
    
    if((close(fdTwo))<0){
        printf("Error closing fdTwo.\n");
        exit(EXIT_FAILURE);
    }
    
    return 0;
    
}

The F_SETFL command macro sets the file status flags to the value of the third argument. We can use logical OR  to combine the bit values of two separate sets of flags; likewise we can use logical AND to remove the bit values from sets of flags.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

int setFlags(int fd, int flags);
int removeFlags(int fd, int flags);
void strFlagReturn(int returnValue);
void printFlags(int fd);


int main(void){
    
    int fdOne = open("another.txt", O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
    
    if(fdOne < 0){
        printf("Error creating file descriptor.\n");
        exit(EXIT_FAILURE);
    }
    
    printFlags(fdOne);
    strFlagReturn(setFlags(fdOne, O_APPEND));
    printFlags(fdOne);
    strFlagReturn(setFlags(fdOne, O_NONBLOCK));
    printFlags(fdOne);
    strFlagReturn(removeFlags(fdOne, O_NONBLOCK));
    printFlags(fdOne);
    
    return 0;
    
}


void strFlagReturn(int returnVal){
    switch(returnVal){
        case 0:
            printf("Flags value altered successfully.\n");
            break;
        case -1:
            printf("Could not get flags to alter them.\n");
            break;
        case -2:
            printf("Could not alter flags after retrieving them.\n");
            break;
    }
    
}
void printFlags(int fd){
    int flags;
    
    if((flags=fcntl(fd, F_GETFL, 0))<0){
        printf("Couldn't access flags for fd %d.\n", fd);
        return;
    } else {
        printf("File Desc. %d: ", fd);
    }
    switch(flags & O_ACCMODE){
        case O_RDONLY:
            printf("Read only. ");
            break;
        case O_WRONLY:
            printf("Write only. ");
            break;
        case O_RDWR:
            printf("Read and write. ");
            break;
    } // end switch-----
    
    if(flags & O_APPEND){
        printf(" Append. ");
    }
    
    if(flags & O_NONBLOCK){
        printf(" Non-blocking.");
    }
    
    printf("\n");
}

//this uses logical and to remove flags
int removeFlags(int fd, int flags){
    int oldFlags;
    
    if((oldFlags = fcntl(fd, F_GETFL, 0))<0){
        return -1;
    }
   
    //AND the flag values
    //and inverse the flags parameter
    flags = ~flags & oldFlags;
    
    if(fcntl(fd, F_SETFL, flags) < 0){
        return -2;
    }
    
    return 0;
    
}

//this uses logical or to add flags
int setFlags(int fd, int flags){
    int oldFlags;
    
    if((oldFlags = fcntl(fd, F_GETFL, 0))<0){
        return -1;
    }
    
    //OR the flags values
    flags = flags | oldFlags;

    if((fcntl(fd, F_SETFL, flags))<0){
        return -2;
    } 
    
    return 0;
}

Note that the only file status flags that can be changed with fcntl() are O_APPEND, O_NONBLOCK, O_SYNC and O_ASYNC.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s