Chapter 5 Question 5 Linux Programming Interface by Michael Kerrisk

Write a program to verify that duplicated file descriptors share a file offset value and open file status flags.

Implementation

Note: this is my own implementation and it has not been peer reviewed. I don't code in C as an occupation so the convention may be off.


/**
 * The Linux Programming Interface by Michael Kerrisk
 *
 * Chapter 5 Question 5
 *
 * Write a program to verify that duplicated file descriptors share a file offset value and open file status flags.
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>


/**
 * Rudimentary error handler used throughout these example problems
 *
 * @param statusCode status code of previous operation, below 0 means error
 * @param errnoSaved errno value
 * @return int -1 for error handling
 */
int rudimentaryErrorHandler(int statusCode, int errnoSaved) {
    if (0 > statusCode) {
        fprintf(stderr, "%s\n", strerror(errnoSaved));
        return -1;
    }

    return 0;
}

int chapterFiveQuestionSix(char *fileName) {
    // Store errno value for system call checks
    int errnoSaved;

    /*
     * Open a file and get the file descriptor, use the flags below
     * O_RDWR open for read/write
     * O_CREAT will create the file if it doesn't exist
     * O_NONBLOCK open file in nonblocking mode
     * O_TRUNC Erase the file every time
     * S_IRUSR read permission bit for the owner of the file 0400
     * S_IWUSR write permissions bit for the owner 0200
     * S_IGRRP read permission bit for teh group owner of the file usually 040
     * S_IWGRP write permission bit for the group owner of the file usually 020
     * S_IROTH read permission bit for other users usually 04
     * S_IWOTH write permission bit for other users usually 02
     */
    int openFd = open(fileName, O_CREAT | O_RDWR | O_NONBLOCK | O_TRUNC,
                      S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

    // Validate open sys call worked
    errnoSaved = errno;
    if (0 > rudimentaryErrorHandler(openFd, errnoSaved)) return -1;

    // Duplicate the open file descriptor
    int duplicatedFd = dup(openFd);

    // Validate duplicateFd works
    errnoSaved = errno;
    if (0 > rudimentaryErrorHandler(duplicatedFd, errnoSaved)) return -1;

    /*
     * Validate that the file descriptors share an offset and file status flags
     * Also, check to ensure the lseek succeeds in both cases
     */
    off_t offSetOpenFd = lseek(openFd, 0, SEEK_CUR);
    errnoSaved = errno;
    if (0 > rudimentaryErrorHandler((int) offSetOpenFd, errnoSaved)) return -1;

    off_t offSetDuplicatedFd = lseek(duplicatedFd, 0, SEEK_CUR);
    errnoSaved = errno;
    if (0 > rudimentaryErrorHandler((int) offSetDuplicatedFd, errnoSaved)) return -1;

    if (offSetOpenFd == offSetDuplicatedFd) {
        fprintf(stdout, "The file descriptors share the same off_t (offset): %d\n", (int) offSetDuplicatedFd);
    }

    /*
     * Get the open file descriptors file flags and validate fcntl succeeded
     */
    int openFdFileFlags = fcntl(openFd, F_GETFL);
    errnoSaved = errno;
    if (0 > rudimentaryErrorHandler(duplicatedFd, errnoSaved)) return -1;

    int duplicatedFdFileFlags = fcntl(duplicatedFd, F_GETFL);
    errnoSaved = errno;
    if (0 > rudimentaryErrorHandler(duplicatedFd, errnoSaved)) return -1;
    
    if (openFdFileFlags == duplicatedFdFileFlags) {
        fprintf(stdout, "The file flags for both descriptors are the same: %d\n", openFdFileFlags);
    }

    // For brevity just closing not validating system calls worked
    close(openFd);
    close(duplicatedFd);

    return 0;
}


int main(int argc, char *argv[]) {
    /*
     * Open a file with the supplied command line parameter, create it if necessary.
     *
     * argv[0] = the program name, i.e. cp
     * argv[1] = the file to write to, create it if doesn't exist
     */
    if (argc <= 1) {
        fprintf(stderr, "usage: %s [fileName]\n", argv[0]);
        return 1;
    }

    // Store a reference pointer in memory to store the filename
    char *fileName = argv[1];

    return chapterFiveQuestionSix(fileName);
}