5.3 - Retrieve and manipulate system files

Retrieving and manipulating system files involves interacting with the operating system’s file system using the standard C library functions.
This functionality revolves around the use of file I/O functions in the stdio.h
library, with the addition of some system-specific APIs (depending on the OS) when more control is needed.
Understanding System Files in C
System files are typically files that are critical to the operation of an operating system or software, such as configuration files, logs, or device files. In C, file handling is done through file streams that represent a communication channel between your program and the file on the disk.
There are two primary ways to interact with files in C:
- Using Standard Library Functions: These functions abstract away the details of file systems and are defined in
stdio.h
. - Using System Calls: These are platform-specific APIs, especially in UNIX-like systems, that allow you to work with files at a lower level.
File I/O Basics Using Standard Library Functions
C’s standard library provides a set of functions to handle files via file pointers (FILE*
). The file pointer is a reference to a file stream, and it is used to track the file’s position in memory and allow operations such as reading, writing, and seeking.
Here are the basic steps to retrieve and manipulate files in C:
2.1 Opening a File
The first step in file manipulation is opening a file. This can be done using the fopen()
function:
FILE *fopen(const char *filename, const char *mode);
- filename: The path to the file you want to open.
- mode: The mode in which you want to open the file.
Common modes include:
"r"
: Open a file for reading (the file must exist)."w"
: Open a file for writing (creates a new file or truncates an existing file)."a"
: Open a file for appending (writes data to the end of the file)."rb"
,"wb"
,"ab"
: Binary modes for reading, writing, or appending.
Example:
FILE *file = fopen("example.txt", "r");
if (file == NULL) {
perror("Error opening file");
}
Reading from a File
To retrieve data from a file, you can use several functions:
-
fgetc(): Reads a single character from the file.
int fgetc(FILE *stream);
Example:
int c; while ((c = fgetc(file)) != EOF) { putchar(c); // Print character to stdout }
-
fgets(): Reads a string from the file up to a newline or EOF.
char *fgets(char *str, int n, FILE *stream);
Example:
char buffer[256]; while (fgets(buffer, sizeof(buffer), file)) { printf("%s", buffer); }
-
fread(): Reads raw data into a buffer, useful for binary files.
size_t fread(void *ptr, size_t size, size_t count, FILE *stream);
Example:
char buffer[512]; size_t bytesRead = fread(buffer, sizeof(char), sizeof(buffer), file);
2.3 Writing to a File
To manipulate a file by writing data, C provides similar functions for output:
-
fputc(): Writes a single character to a file.
int fputc(int char, FILE *stream);
-
fputs(): Writes a string to a file.
int fputs(const char *str, FILE *stream);
-
fwrite(): Writes raw data to a file.
size_t fwrite(const void *ptr, size_t size, size_t count, FILE *stream);
Example:
FILE *file = fopen("output.txt", "w");
if (file != NULL) {
fputs("Hello, World!", file);
fclose(file);
}
Closing a File
After file operations are complete, you should always close the file to ensure that all data is flushed from the buffer and any resources are released.
int fclose(FILE *stream);
Example:
fclose(file);
Error Handling
To check for errors during file operations, you can use:
-
perror(): Prints a human-readable error message.
perror("File error");
-
feof(): Checks if the end of the file has been reached.
int feof(FILE *stream);
-
ferror(): Checks if an error has occurred during file operations.
int ferror(FILE *stream);
File Manipulation Beyond the Standard Library
The standard library covers most basic operations. However, more advanced file manipulations (like permissions, file locking, and non-blocking I/O) require system-specific functions.
POSIX File System Functions (UNIX/Linux)
If you are working in a UNIX-like environment, POSIX system calls provide lower-level control over files:
-
open(): Opens a file descriptor.
int open(const char *pathname, int flags);
-
read(): Reads from a file descriptor.
ssize_t read(int fd, void *buf, size_t count);
-
write(): Writes to a file descriptor.
ssize_t write(int fd, const void *buf, size_t count);
-
close(): Closes a file descriptor.
int close(int fd);
These calls provide greater control than fopen()
and fread()
/fwrite()
, allowing you to work with file descriptors (integers) instead of file streams (FILE*
).
File Metadata Operations
To retrieve and manipulate file metadata (permissions, modification times, etc.), system-specific functions like stat()
can be used in POSIX systems:
-
stat(): Gets file metadata (e.g., size, modification time).
int stat(const char *path, struct stat *buf);
Error Codes and Return Values
System-level functions often return error codes. These are typically integers that indicate the type of error (e.g., EACCES
for permission denied). You can retrieve and interpret these codes using errno
:
#include <errno.h>
#include <string.h>
#include <stdio.h>
if (open("file.txt", O_RDONLY) == -1) {
printf("Error: %s\n", strerror(errno));
}
Manipulating System Files (Advanced Scenarios)
Beyond basic file I/O, advanced file manipulation might include:
Changing File Permissions
You can change file permissions using chmod()
on POSIX systems:
int chmod(const char *pathname, mode_t mode);
Example:
chmod("example.txt", 0644); // Read-write for owner, read-only for others
File Locking
To prevent multiple processes from writing to the same file concurrently, you can lock files using flock()
or fcntl()
:
int flock(int fd, int operation);
This allows you to create advisory locks on a file, which can be shared (read lock) or exclusive (write lock).
Memory Mapping Files
For performance-critical applications, you might want to map files directly into memory using mmap()
(available on UNIX-like systems). This allows you to treat the contents of a file as if it were part of your program’s memory.
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
Summary
File retrieval and manipulation follow the well-established practices of standard C, emphasizing the use of file streams via
FILE*
and various functions likefopen()
,fread()
,fwrite()
, andfclose()
.
For more advanced file operations, system-level calls such as
open()
,read()
,stat()
, andchmod()
provide deeper control, especially in UNIX-like systems. Proper error handling, file locking, and memory mapping are essential techniques for robust system file manipulation.
By combining standard library functions with system-specific APIs, C provides a comprehensive toolkit for interacting with the file system, allowing you to perform a wide range of tasks, from basic file I/O to complex file management operations.