Section 5: File Operations

In this section, we will dive into file operations, which will be helpful for your work on the caching I/O project.

You can find the code for this section here.

Discussion Activity: Describing (File) Descriptors

DISCUSSION A. What are file descriptors? How does the operating system use file descriptors to represent files on disk?

DISCUSSION B. What operations can you perform on file descriptors? What are the system calls that perform these operations?

Question 1: The Writing's on the Wall

In this portion of the section, you will examine a series of short programs that involve writing to files. Each file contains the same text, "0123456789... This file is 55 characters (bytes) long!\n". For each of the programs, identify:

We encourage you to run the code in question number # by running make run-#. This will also display the file before and after the program runs. For example, make run-1a will run the code for question 1A.

QUESTION 1A. Consider the following C code:

int main() {
    int fd = open("files/file1a.txt", O_RDWR);

    // Edit the file contents
    fd[0] = '1';
    fd[1] = " byte was written!\n";

    close(fd);

    return 0;
}

The contents of file1a.txt is "0123456789... This file is 55 characters (bytes) long!\n".

QUESTION 1B. Consider the following C code:

int main() {
    int fd = open("files/file1b.txt", O_RDWR);

    // Determine how long the file is
    struct stat stats;
    fstat(fd, &stats);
    printf("The file is %ld bytes long.\n", stats.st_size);

    // Write data to end of file
    char data[] = "This is 26 bytes of data.\n";
    lseek(fd, 0, SEEK_END);
    write(fd, data, strlen(data));

    // Determine how long the file is now
    fstat(fd, &stats);
    printf("The file is now %ld bytes long.\n", stats.st_size);

    close(fd);

    return 0;
}

The contents of file1b.txt is "0123456789... This file is 55 characters (bytes) long!\n".

QUESTION 1C. Consider the following C code:

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

int main() {
    int fd = open("files/file1c.txt", O_RDWR | O_APPEND);

    // Write data to the file
    char data[] = "8 bytes\n";
    write(fd, data, 8);

    // Read data from the same location
    lseek(fd, -15, SEEK_END);
    char buf[16];
    memset(buf, '\0', 16);
    int bytes_read = read(fd, buf, 15);
    buf[bytes_read] = '\0';

    // Print each character individually in case we have null bytes
    printf("The data read was: ");
    for (int i = 0; i < 16; i++) {
        printf("%c", buf[i]);
    }
    printf(". It is %d bytes.\n", bytes_read);

    close(fd);

    return 0;
}

The contents of file1c.txt is "0123456789... This file is 55 characters (bytes) long!\n".

Question 2: Not All Who Seek are Lost

In this portion of the section, you will examine a series of short programs that involve seeking to files. Each file contains the same text, "0123456789... This file is 55 characters (bytes) long!\n". For each of the programs, identify:

QUESTION 2A. Consider the following C code:

int main() {
    int fd = open("files/file2a.txt", O_RDWR);

    // Seek to beyond end of file
    lseek(fd, 128, SEEK_SET);

    // Read data from file
    char buf[16];
    memset(buf, '\0', 16);
    int bytes_read = read(fd, buf, 15);
    buf[bytes_read] = '\0';

    // Print each character individually in case we have null bytes
    printf("The data read was: ");
    for (int i = 0; i < 16; i++) {
        printf("%c", buf[i]);
    }
    printf(". It is %d bytes.\n", bytes_read);

    close(fd);

    return 0;
}

The contents of file2a.txt is "0123456789... This file is 55 characters (bytes) long!\n".

QUESTION 2B. Consider the following C code:

int main() {
    int fd = open("files/file2b.txt", O_RDWR);

    // Seek to beyond end of file
    lseek(fd, 128, SEEK_SET);

    // Write data to file
    char data[] = "This is 18 bytes!\n";
    write(fd, data, 18);

    close(fd);

    return 0;
}

The contents of file2b.txt is "0123456789... This file is 55 characters (bytes) long!\n".