Mar 12, 2022

C programming file descriptors

File access is an essential feature of any programming language. In C we can use two methods to access the file system. Those are file descriptors and file streams. In this document, we are going to see how we can use file descriptors to access files.


With file descriptors, we have some functions like open, close, read, write, etc. to access files. A file descriptor is actually an integer number. Every opened file has its own unique number. We call it a file descriptor. When we open a file with the open() function it returns a file descriptor. Then we can use this file descriptor to perform further operations on that file. For example, if we want to read data from that opened file we use the given file descriptor as an argument for the read function.

Let's talk more about the open function. The first argument for this function is the name of the file we want to open. Then we supply some other arguments to specify the mode of opening. I mentioned it will return a file descriptor when opening a file But this is not happening every time. If it fails to open the file return value will be -1. So before we do further operations on opened file we must check the return value of the open() function. If it is equal to -1 we know the open() function couldn't open the file.

In the following code, we can see the usage of the open() function.

#include 
#include 
#include 
#include 


int main(int argc, char const *argv[])
{
 int fd;
 char buff[1000];
 
 if ((fd = open("file.txt",  O_RDONLY)) == -1)
 {
  printf("file opening failed\n");
  exit(0);
 }else{
  printf("file opening successful\n");
  printf("file descriptor: %d\n", fd);
 }
}

(The string.h header file and declared character array are not required at now. those are for the next steps.)

At the top of the main function, we have declared an integer variable called fd. this is to hold the file descriptor value. Then we have used an if statement to check the return value of the open function. If it is -1, we display an error and exit the program. If not we can continue the program. The first argument to open function is "file.txt". This is the physical name of the file. Next, we have specified O_RDONLY. This indicates we want to open the file in read-only mode. Here are some other arguments we can use.

O_RDONLY This instructs the open function to Open files in read-only mode. If we open a file in read-only mode and try to write something, the write function will fail to do it and return -1.

O_WRONLY This will open file for write-only access.
O_RDWR This option can be used to open files for both read and write access.

 

O_CREAT This mode can be used to create the file if it doesn’t exist.

O_APPEND If we use this option all data we write to the file will append to the end of the file. If there are already some data in the file they will keep like that.

O_TRUNC If the file already exists with the specified name it will be truncated.

 


Next, we use the read function to read the content of a file. Following is the definition of the function.

ssize_t read(int fd, void *buf, size_t count);


Its first argument is a file descriptor. We have to give the previously opened file's descriptor as the argument. The second argument is a pointer. Read function read the content from the given file and save it on this pointed location. We can give the address of the previously allocated buffer space as this pointer. The third argument is the number of bytes to read from the file. This file returns the number of bytes it read from the file if it successfully read the file. If it fails it will return -1.

The following code shows the usage of reading function.

if (read(fd, buff, sizeof(buff)) == -1)
{
 printf("Error while reading file\n");
 exit(0);
}else{
 printf("File contents: %s\n", buff);
}

You know the name of character buffer space "buff" is a pointer to that buffer space. Also, we used the sizeof() function to get buffer size. We read data from the file and save it in the buffer space.

Let's see complete code to read a file.

#include 
#include 
#include 
#include 

int main(int argc, char const *argv[])
{
 int fd;
 char buff[1000];
 
 if ((fd = open("file.txt",  O_RDONLY)) == -1)
 {
  printf("file opening failed\n");
  exit(0);
 }else{
  printf("file opening successful\n");
  printf("file descriptor: %d\n", fd);





  if (read(fd, buff, sizeof(buff)) == -1)
  {
   printf("Error while reading file\n");
   exit(0);
  }else{
   printf("File contents: %s\n", buff);
  }

 }

 return 0;
}

 

I compile and run it on a Linux mashing. Also before running it, I created a file named "file.txt" with some sample text using the echo command.

[email protected]:/c# nano fd.c
[email protected]:/c# echo "Hi, this is a sample text in the file" > file.txt
[email protected]:/c# cat file.txt
Hi, this is a sample text in the file

[email protected]:/c# gcc fd.c -o fd
[email protected]:/c# ./fd
file opening successful
file descriptor: 3
File contents: Hi, this is a sample text in the file

 

 

Awesome. It worked as expected.

Next, I write something to the file.

#include 
#include 
#include 
#include 

int main(int argc, char const *argv[])
{
 int fd;
 char buff[] = "Hi, I'm going to written into the file\n";

 if ((fd = open("file.txt",  O_WRONLY)) == -1)
 {
  printf("file opening failed\n");
  exit(0);
 }else{
  printf("file opening successful\n");
  printf("file descriptor: %d\n", fd);





  if (write(fd, buff, strlen(buff)) == -1)
  {
   printf("Error while writing to file\n");
   exit(0);
  }else{
   printf("wrote %d bytes to the file:\n", strlen(buff));
  }

 }


 return 0;
}

 

write() function takes three arguments. The first is a file descriptor. The second is a pointer to a location. The function reads data from this location and writes it to the file. The third argument specifies how many bytes we should write in the file.

Let's compile and run it to see if it work.

[email protected]:/c# nano fd.c
[email protected]:/c# gcc fd.c -o fd

[email protected]:/c# touch file.txt
[email protected]:/c# cat file.txt

[email protected]:/c# ./fd
file opening successful
file descriptor: 3
wrote 39 bytes to the file:

[email protected]:/c# cat file.txt
Hi, I'm going to written into the file

 


It is a good programming practice to close the file descriptor after we have done the operation on opened file.
We can use the close() function to do this It only takes the file descriptor as the argument.

So in this document, we saw how we can use file descriptors to access files. In the next article, we are going to see how we can use file streams.

Jun 22
Protostar Stack0 walkthrough

Hello there, In this tutorial we are going to learn Linux exploit development. We use protostar....

Mar 13
Reverse TCP shell with Metasploit

Metasploit is an awesome tool. It can be used to automate the exploitation process, generate....

Mar 08
Complex number program in C++ using class

In this tutorial, we are going to see how we can write a complex number program in c++ using the....

Replying to 's comment Cancel reply
ABOUT AUTHOR
Thilan Danushka Dissanayaka

Thilan Dissanayaka

Hi, I'm Thilan from Srilanka. An undergraduate Engineering student of University of Ruhuna. I love to explorer things about CS, Hacking, Reverse engineering etc.

CATEGORIES
SOCIAL
RANDOM ARTICLES