Two functions,
read()
and
write()
, are provided in
<unistd.h>
. The
write()
function writes X number of bytes from a buffer, and the
read()
function tries to read up to X number of bytes, and stores the data in the specified buffer. Both functions require a file descriptor to work and return -1 if the action has failed.
Partial reads are quite common. The
read()
function should be checked for two conditions: a negative return value, if it has failed, and 0, if it has reached EOF.
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <cstring>
#include <stdio.h>
#include <stddef.h>
using namespace std;
const int read_buff_length = 16;
int main(void) {
int lineCount = 0;
int writeFd, readFd, returnVal;
char *path = "test.txt";
char writeBuffer[] = "You, the people have the power - the power to create machines. The power to create happiness! You, the people, have the power to make this life free and beautiful, to make this life a wonderful adventure.";
char readBuffer[read_buff_length+1];
//----write to the file------------------------------------
if((writeFd = open(path, O_CREAT | O_TRUNC | O_WRONLY)) < 0){
printf("Error opening %s.\n", path);
exit(EXIT_FAILURE);
} else {
printf("File opened!\n");
returnVal = write(writeFd, writeBuffer, sizeof(writeBuffer) / sizeof(char));
if(returnVal < 0){
printf("Error writing to file.\n");
exit(EXIT_FAILURE);
} else {
printf("File written to!\n");
if((close(writeFd))<0){
printf("Error closing %s.\n", path);
exit(EXIT_FAILURE);
} else {
printf("%s closed after writing.\n", path);
//---------read from the file----------------------------------
if((readFd = open(path, O_RDONLY))<0){
printf("Error opening %s.\n", path);
exit(EXIT_FAILURE);
} else {
printf("%s opened!\n", path);
while(true){
returnVal = read(readFd, readBuffer, read_buff_length);
if(returnVal>0){
readBuffer[read_buff_length+1]='';
printf("Line %d: %s\n", ++lineCount, readBuffer);
} else {
if(returnVal==0){
printf("Read completed.\n");
break;
} else {
printf("Problem reading file.\n");
exit(EXIT_FAILURE);
if((close(writeFd))<0){
printf("Error closing %s.\n", path);
exit(EXIT_FAILURE);
} else {
printf("%s closed after reading.\n", path);
return 0;
The third argument sent to read() and write() is size_t, which defines the number of bytes to be read in or written to the supplied buffer. The size_t type is required by POSIX and is used to store values used in measuring sizes in bytes. The ssize_t type is a signed version of size_t, and is used as the return value type of the read() and write() functions. The maximum value for size_t is SIZE_MAX and the maximum value of ssize_t is SSIZE_MAX. The value of SSIZE_MAX is quite large for a single read or write.
#include <stdio.h>
#include <limits.h>
int main(void){
printf("SSIZE_MAX = %ld\n", SSIZE_MAX);
return 0;
Note that SSIZE_MAX is found in the limits.h header file.
The open() system call can take a third argument; this argument is the file’s mode, of type mode_t, which is defined as the types.h header file. We can define the mode_t value by OR-ing one or more of the symbolic constants from <sys/stat.h>.
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <cstring>
#include <stdio.h>
#include <sys/stat.h>
using namespace std;
int main(void) {
printf("User permissions: \n");
printf("S_IRWXU = %d\n", S_IRWXU);//448
printf("S_IRUSR = %d\n", S_IRUSR);//256
printf("S_IWUSR = %d\n", S_IWUSR);//128
printf("S_IXUSR = %d\n\n", S_IXUSR);//64
printf("Group permissions: \n");
printf("S_IRWXG = %d\n", S_IRWXG);//56
printf("S_IRGRP = %d\n", S_IRGRP);//32
printf("S_IWGRP = %d\n", S_IWGRP);//16
printf("S_IXGRP = %d\n\n", S_IXGRP);//08
printf("Other permissions: \n");
printf("S_IRWXO = %d\n", S_IRWXO);//07
printf("S_IROTH = %d\n", S_IROTH);//04
printf("S_IWOTH = %d\n", S_IWOTH); //02
printf("S_IXOTH = %d\n\n", S_IXOTH); //01
return 0;
The S_IRWXU permission allows users to read, write and execute a file; the S_IRWXG permission allows group members to read, write and execute a file, and the S_IRWXO permission allows others to read, write and execute a file.
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
int main(void){
char buffer[256];
char *noWrite = "fileOne";
char *noRead = "fileTwo";
//can write, not read
mode_t modeOne = S_IWUSR;
//can read, not write
mode_t modeTwo = S_IRUSR;
int fileDescriptor;
//-r--------
if((fileDescriptor = open(noWrite, O_CREAT | O_RDONLY,modeTwo))<0){
printf("Could not open file %s.\n", noWrite);
exit(EXIT_FAILURE);
if(close(fileDescriptor)<0){
printf("Could not close file %s.\n", noWrite);
exit(EXIT_FAILURE);
//--w-------
if((fileDescriptor = open(noRead, O_CREAT | O_WRONLY, modeOne))<0){
printf("Could not open file %s.\n", noRead);
exit(EXIT_FAILURE);
if(close(fileDescriptor)<0){
printf("Could not close file %s.\n", noRead);
exit(EXIT_FAILURE);
//try to open the read file for writing
if((fileDescriptor = open(noWrite, O_WRONLY))<0){
printf("Could not open %s for writing.\n", noWrite);
//try to open the write file for reading
if((fileDescriptor = open(noRead, O_RDONLY))<0){
printf("Could not open %s for reading.\n", noRead);
return 0;
Note that the umask defines what permissions cannot be allowed when new files are created. To allow for more permissions than a umask will allow, we must change the permissions after the file has been created.
When a file descriptor is opened in append mode, via the O_APPEND access flag, writes do not occur at the file descriptor’s current file position. Instead, writes occur at the end of the file.
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main(void){
int i = 0;
pid_t pid;
ssize_t retVal;
int fd;
char buffer[256];
char *path = "xyz";
printf("Starting in process %lld\n", getpid());
if((fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, S_IRWXU))<0){
printf("Error creating file %s\n", path);
exit(EXIT_FAILURE);
} else {
if(!(close(fd)<0)){
printf("File %s created.\n", path);
if((pid=fork())<0){
printf("Could not fork process.\n");
exit(EXIT_FAILURE);
//in child process
if(pid==0){
if((fd = open(path, O_WRONLY | O_APPEND))<0){
printf("Error appending to file %s in %lld\n", path, getpid());
exit(EXIT_FAILURE);
while(i++ < 5){
sprintf(buffer, "Hello from process %d \t (child %d).\n", getpid(), i);
if((retVal = write(fd, buffer, strlen(buffer)))<0){
printf("Error writing to file %s in %lld\n", path, getpid());
exit(EXIT_FAILURE);
printf("Writing to file from process %lld.\n", getpid());
sleep(i);
if(close(fd)<0){
printf("Error closing file %s in %lld\n", path, getpid());
exit(EXIT_FAILURE);
//in parent process
if(pid>0){
if((fd = open(path, O_WRONLY | O_APPEND))<0){
printf("Error appending to file %s in %lld\n", path, getpid());
exit(EXIT_FAILURE);
while(i++ < 5){
sprintf(buffer, "Hello from process %d \t (parent %d).\n", getpid(), i);
if((retVal = write(fd, buffer, strlen(buffer)))<0){
printf("Error writing to file %s in %lld\n", path, getpid());
exit(EXIT_FAILURE);
printf("Writing to file from process %lld\n", getpid());
sleep(i + 2);
if(close(fd)<0){
printf("Error closing file %s in %lld\n", path, getpid());
exit(EXIT_FAILURE);
return 0;
Alright, that’s enough mischief for today. When we next look at C in the *nix environment, we will look at lseek() as well inspecting and modifying file and directory metadata.
Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use.
To find out more, including how to control cookies, see here:
Cookie Policy