Unit 3 - Notes

CSE325

Unit 3: File system management

Introduction to File System Calls

File system calls provide the interface between a running program (process) in User Space and the Operating System Kernel. In Linux/Unix systems, almost everything is treated as a file. To manipulate files, the kernel provides a set of system calls.

Unlike standard library functions (like fopen, fprintf in C), system calls work directly with File Descriptors (FDs) rather than FILE* streams. These calls are unbuffered and closer to the hardware, offering lower-level control over file I/O.

A conceptual block diagram showing the interaction between User Space and Kernel Space during a file...
AI-generated image — may contain inaccuracies


1. File Descriptors

Before diving into specific calls, it is essential to understand the File Descriptor.

  • Definition: A non-negative integer representing an open file.
  • Standard FDs:
    • 0: Standard Input (stdin)
    • 1: Standard Output (stdout)
    • 2: Standard Error (stderr)
  • Assignment: When a new file is opened, the OS assigns the lowest available integer (usually starts at 3 for user files).

A detailed diagram of the Process Control Block (PCB) linking to the File Descriptor Table. On the l...
AI-generated image — may contain inaccuracies


2. The open() System Call

The open() system call is used to establish a connection between a file and a file descriptor. It can open an existing file or create a new one.

Syntax

C
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

Arguments

  1. pathname: The relative or absolute path to the file.
  2. flags: Determines how the file is opened. Common flags (combined using bitwise OR |):
    • O_RDONLY: Open for reading only.
    • O_WRONLY: Open for writing only.
    • O_RDWR: Open for reading and writing.
    • O_CREAT: Create the file if it doesn't exist.
    • O_EXCL: Used with O_CREAT; fails if file already exists.
    • O_TRUNC: If file exists, truncate size to 0 (delete contents).
    • O_APPEND: Append data to the end of the file.
  3. mode: (Required only when creating a file). Specifies permissions (e.g., 0644 - Read/Write for owner, Read for group/others).

Return Value

  • Success: Returns the new file descriptor (a non-negative integer).
  • Failure: Returns -1 and sets the global variable errno.

Example

C
int fd = open("data.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd < 0) {
    perror("Error opening file");
}


3. The close() System Call

The close() system call dissociates a file descriptor from the corresponding file, releasing resources.

Syntax

C
#include <unistd.h>

int close(int fd);

Arguments

  • fd: The file descriptor to close.

Return Value

  • 0: Success.
  • -1: Error (e.g., invalid FD).

Importance

Failing to close files can lead to a "resource leak." The OS has a limit on the number of files a process can keep open simultaneously.


4. The read() System Call

The read() system call attempts to read a specified number of bytes from the file into a buffer.

Syntax

C
#include <unistd.h>

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

Arguments

  1. fd: File descriptor to read from.
  2. buf: Pointer to the memory buffer where data will be stored.
  3. count: Maximum number of bytes to read.

Return Value

  • > 0: Number of bytes actually read.
  • 0: End of File (EOF) reached.
  • -1: Error.

Note on ssize_t

ssize_t is a signed integer type used to handle the -1 error return. size_t is unsigned.


5. The write() System Call

The write() system call writes data from a buffer to the file associated with the file descriptor.

Syntax

C
#include <unistd.h>

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

Arguments

  1. fd: File descriptor to write to.
  2. buf: Pointer to the data to be written.
  3. count: Number of bytes to write.

Return Value

  • > 0: Number of bytes actually written.
  • -1: Error (e.g., disk full, file opened read-only).

A flowchart diagram depicting the logic of a file copy program using read() and write(). Start node ...
AI-generated image — may contain inaccuracies


6. The lseek() System Call

lseek stands for "long seek". It repositions the read/write file offset (the "cursor" position inside the file).

Syntax

C
#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);

Arguments

  1. fd: File descriptor.
  2. offset: Number of bytes to move the pointer (can be negative).
  3. whence: The reference point for the offset.
    • SEEK_SET: The beginning of the file.
    • SEEK_CUR: The current position of the file pointer.
    • SEEK_END: The end of the file.

Return Value

  • Success: Returns the new offset location in bytes from the beginning of the file.
  • Failure: Returns -1.

Common Uses

  • lseek(fd, 0, SEEK_SET): Rewind to the start.
  • lseek(fd, 0, SEEK_END): Move to the end (useful for appending or finding file size).
  • lseek(fd, -10, SEEK_CUR): Move backward 10 bytes.

A visual diagram illustrating the `lseek` directive options. A horizontal bar represents a File, div...
AI-generated image — may contain inaccuracies


Comprehensive Example: Implementing all calls

This C program creates a file, writes to it, uses lseek to reset the cursor, reads the content back, and prints it to the standard output.

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

#define FILENAME "example_file.txt"
#define BUFFER_SIZE 100

int main() {
    int fd;
    char write_buf[] = "Operating Systems Lab - Unit 3: File Management.";
    char read_buf[BUFFER_SIZE];
    ssize_t bytes_read, bytes_written;

    // 1. OPEN (Create and Write)
    // O_TRUNC clears file if it exists so we start fresh
    fd = open(FILENAME, O_RDWR | O_CREAT | O_TRUNC, 0644);
    
    if (fd < 0) {
        perror("Open failed");
        exit(1);
    }
    printf("File opened with FD: %d\n", fd);

    // 2. WRITE
    // Write the contents of write_buf to the file
    bytes_written = write(fd, write_buf, strlen(write_buf));
    
    if (bytes_written < 0) {
        perror("Write failed");
        close(fd);
        exit(1);
    }
    printf("Written %ld bytes to file.\n", bytes_written);

    // 3. LSEEK
    // After writing, the cursor is at the end. Move it back to start to read.
    if (lseek(fd, 0, SEEK_SET) < 0) {
        perror("Lseek failed");
        close(fd);
        exit(1);
    }
    printf("Cursor reset to beginning of file.\n");

    // 4. READ
    // Read from the file into read_buf
    bytes_read = read(fd, read_buf, BUFFER_SIZE - 1); // Leave space for null terminator
    
    if (bytes_read < 0) {
        perror("Read failed");
        close(fd);
        exit(1);
    }

    // Null terminate the buffer to print it as a string
    read_buf[bytes_read] = '\0';
    
    printf("Read from file: \"%s\"\n", read_buf);

    // 5. CLOSE
    if (close(fd) < 0) {
        perror("Close failed");
        exit(1);
    }
    printf("File closed successfully.\n");

    return 0;
}

Explanation of the Code Flow

  1. open: Creates example_file.txt with read/write permissions (0644).
  2. write: Writes the string literal into the file. The file offset moves forward as data is written.
  3. lseek: Because the offset is at the end of the written data, we must move it back to 0 (SEEK_SET) to read what we just wrote.
  4. read: Reads the data from the disk into the read_buf memory.
  5. close: Releases the file descriptor fd so it can be reused by the system.