Projects/3BIT/winter-semester/ISA/include/file_io.h
2026-04-14 19:28:46 +02:00

830 lines
28 KiB
C

/*
* ==================================================================================
* File I/O Header - File Management and Transfer Operations
*
* ISA Project
* Author: Roman Necas (xnecasr00)
* Date: 13. October 2025
*
* PURPOSE:
* This header defines the file management layer for the ICMP covert channel
* file transfer application. It provides abstractions for chunk-based file
* reading/writing, path validation, and transfer statistics tracking.
*
* RESPONSIBILITIES:
* - File opening and validation (read/write modes)
* - Chunk-based file I/O for network transmission
* - Fragment tracking for multi-packet transfers
* - Path normalization and security validation
* - File metadata preservation (permissions, timestamps)
* - Transfer statistics and progress tracking
*
* SECURITY FEATURES:
* - Path traversal prevention (../.. attacks)
* - Permission validation before file access
* - Atomic file operations (temp file + rename)
* - Secure file deletion with overwrite
* - Basename extraction to prevent path disclosure
*
* CHUNK-BASED I/O:
* Files are read and written in chunks to match network payload size
* (MAX_PAYLOAD_SIZE = 1400 bytes). This allows efficient streaming of
* large files without loading entire file into memory.
*
* FRAGMENTATION SUPPORT:
* Large files are automatically divided into fragments, each containing
* one chunk of data. The file_manager_t structure tracks fragment numbers
* for proper reassembly on the receiving end.
* ==================================================================================
*/
#ifndef FILE_IO_H
#define FILE_IO_H
#include "common.h"
/*
* ==================================================================================
* FILE MANAGER STRUCTURE
* ==================================================================================
*/
/*
* file_manager_t - File I/O state management
*
* This structure encapsulates all state required for chunk-based file operations.
* It supports both reading (client mode - sending file) and writing (server mode -
* receiving file) with automatic fragment tracking.
*
* LIFECYCLE:
* 1. Allocated by caller (typically on stack or in session structure)
* 2. Initialized by file_manager_open_read() or file_manager_open_write()
* 3. Used for file_manager_read_chunk() or file_manager_write_chunk() calls
* 4. Cleaned up by file_manager_cleanup() (closes file, frees memory)
*
* USAGE PATTERNS:
*
* Client mode (reading file to send):
* file_manager_t fm;
* file_manager_open_read(&fm, "/path/to/file.txt");
* while (!file_manager_is_complete(&fm)) {
* uint8_t chunk[MAX_PAYLOAD_SIZE];
* size_t bytes_read;
* file_manager_read_chunk(&fm, chunk, MAX_PAYLOAD_SIZE, &bytes_read);
* send_data_packet(chunk, bytes_read);
* }
* file_manager_cleanup(&fm);
*
* Server mode (writing received file):
* file_manager_t fm;
* file_manager_open_write(&fm, "received_file.txt");
* file_manager_set_file_size(&fm, expected_size); // From metadata
* for each received chunk:
* file_manager_write_chunk(&fm, chunk_data, chunk_size);
* file_manager_cleanup(&fm);
*
* THREAD SAFETY:
* This structure is NOT thread-safe. Each file transfer should have its own
* file_manager_t instance.
*
* MEMORY MANAGEMENT:
* The filename field is dynamically allocated by file_manager_open_*() and
* freed by file_manager_cleanup(). Always call cleanup to prevent leaks.
*/
typedef struct {
/*
* file_handle - Standard C FILE pointer for I/O operations
*
* This is the underlying file stream used for all read/write operations.
* Opened by fopen() in file_manager_open_read() or file_manager_open_write().
*
* Modes:
* - Read mode: fopen(filename, "rb") - binary read
* - Write mode: fopen(filename, "wb") - binary write (overwrites existing)
*
* Values:
* - Non-NULL: Valid open file
* - NULL: No file open or error occurred
*
* Closed by file_manager_cleanup() using SAFE_CLOSE macro (fclose).
*/
FILE *file_handle;
/*
* filename - Full path to the file
*
* Dynamically allocated string containing the complete path to the file
* being read or written. This is used for error messages and logging.
*
* Allocation:
* - Allocated by strdup() in file_manager_open_read() or file_manager_open_write()
* - Freed by file_manager_cleanup() using SAFE_FREE macro
*
* Content:
* - Client mode: Path specified by user (can include directories)
* - Server mode: Basename only (extracted from metadata, no path components)
*
* Security note:
* Server mode ALWAYS strips directory components to prevent path traversal
* attacks (e.g., "../../../etc/passwd" becomes "passwd").
*/
char *filename;
/*
* file_size - Total size of the file in bytes
*
* Client mode: Determined by stat() or fseek(SEEK_END) during file_manager_open_read()
* Server mode: Set by file_manager_set_file_size() from START packet metadata
*
* Used to:
* - Calculate total_fragments (file_size / chunk_size + 1)
* - Verify transfer completion (bytes_processed == file_size)
* - Display progress percentage
* - Pre-allocate space on server (optional)
*
* Range: 0 to UINT64_MAX (theoretical max ~16 exabytes)
* Practical limit: Limited by filesystem and available disk space
*/
uint64_t file_size;
/*
* bytes_processed - Number of bytes read or written so far
*
* Incremented by file_manager_read_chunk() or file_manager_write_chunk()
* after each successful operation.
*
* Used to:
* - Track transfer progress: (bytes_processed / file_size) * 100
* - Detect completion: (bytes_processed >= file_size)
* - Calculate current file position for seeking
* - Verify expected vs actual bytes received (server mode)
*
* Invariant: bytes_processed <= file_size (violation indicates corruption)
*/
uint64_t bytes_processed;
/*
* total_fragments - Total number of fragments for transfer
*
* Calculated during file_manager_open_read() as:
* total_fragments = (file_size + chunk_size - 1) / chunk_size
*
* This is the ceiling division, ensuring all bytes are covered.
*
* Examples (chunk_size = 1400):
* - 500 byte file: 1 fragment
* - 1400 byte file: 1 fragment
* - 1401 byte file: 2 fragments
* - 1 MB file: 750 fragments
*
* Used to:
* - Populate fragment_total field in IFTP header
* - Display progress: "Fragment 500/750"
* - Server validation: Verify received fragment numbers are valid
*/
uint32_t total_fragments;
/*
* current_fragment - Current fragment being processed (0-based)
*
* Client mode: Incremented after each file_manager_read_chunk() call
* Server mode: Set based on fragment_current field in received DATA packet
*
* Range: 0 to (total_fragments - 1)
*
* Used to:
* - Populate fragment_current field in IFTP header
* - Track read position in file
* - Detect duplicate or out-of-order fragments (server mode)
*
* Invariant: current_fragment < total_fragments (unless complete)
*/
uint32_t current_fragment;
/*
* read_mode - File operation mode flag
*
* Values:
* - 1: Read mode (client sending file)
* - 0: Write mode (server receiving file)
*
* Determines:
* - Which operations are valid (read vs write)
* - How file_size is determined (stat vs metadata)
* - Whether fragments are generated (read) or validated (write)
* - Error message context
*
* Set by file_manager_open_read() or file_manager_open_write().
*/
int read_mode;
/*
* file_permissions - File permissions for preservation
*
* POSIX file mode bits (mode_t from sys/stat.h):
* - Owner: read (0400), write (0200), execute (0100)
* - Group: read (0040), write (0020), execute (0010)
* - Other: read (0004), write (0002), execute (0001)
* - Special: setuid (04000), setgid (02000), sticky (01000)
*
* Client mode:
* - Read from source file using stat() in file_manager_open_read()
* - Transmitted in START packet metadata (future enhancement)
*
* Server mode:
* - Received from START packet metadata (future enhancement)
* - Applied to written file using chmod() in file_manager_cleanup()
*
* Default: 0644 (rw-r--r--) if not specified
*
* Security note: Permissions are sanitized to prevent setuid/setgid
* on received files.
*/
mode_t file_permissions;
/*
* last_modified - Last modification time (Unix timestamp)
*
* Seconds since Unix epoch (1970-01-01 00:00:00 UTC).
*
* Client mode: Read from source file using stat()
* Server mode: Can be set on received file using utime() (future enhancement)
*
* Used to preserve file metadata across transfer.
*/
time_t last_modified;
} file_manager_t;
/*
* ==================================================================================
* FILE TRANSFER STATISTICS STRUCTURE
* ==================================================================================
*/
/*
* file_transfer_stats_t - File transfer performance metrics
*
* This structure tracks timing and throughput statistics for a file transfer
* session. It complements network_stats_t which tracks packet-level metrics.
*
* LIFECYCLE:
* 1. Initialized by file_transfer_stats_init() (zeros fields, sets start_time)
* 2. Updated by file_transfer_stats_update() after each chunk transfer
* 3. Finalized by file_transfer_stats_finalize() (sets end_time, calculates rate)
* 4. Displayed by file_transfer_stats_print()
*
* USAGE:
* file_transfer_stats_t stats;
* file_transfer_stats_init(&stats);
* stats.total_bytes = file_size;
* // ... transfer chunks ...
* file_transfer_stats_update(&stats, chunk_size);
* file_transfer_stats_finalize(&stats);
* file_transfer_stats_print(&stats);
*/
typedef struct {
/*
* total_bytes - Total bytes to transfer
*
* Set from file_manager_t.file_size at start of transfer.
* Used to calculate progress percentage.
*/
uint64_t total_bytes;
/*
* bytes_transferred - Bytes successfully transferred
*
* Incremented by file_transfer_stats_update() after each chunk.
* Should equal total_bytes when transfer completes.
*/
uint64_t bytes_transferred;
/*
* fragments_sent - Number of fragments sent (client mode)
*
* Incremented after each DATA packet transmission.
* Used to track progress: "Sent fragment 500/750"
*/
uint32_t fragments_sent;
/*
* fragments_received - Number of fragments received (server mode)
*
* Incremented after each DATA packet reception and write.
* May be less than expected if transfer is interrupted.
*/
uint32_t fragments_received;
/*
* start_time - Transfer start timestamp (Unix time)
*
* Set by file_transfer_stats_init() using time(NULL).
* Used to calculate total transfer duration.
*/
time_t start_time;
/*
* end_time - Transfer end timestamp (Unix time)
*
* Set by file_transfer_stats_finalize() using time(NULL).
* Used to calculate total transfer duration.
*/
time_t end_time;
/*
* transfer_rate - Transfer rate in bytes per second
*
* Calculated by file_transfer_stats_finalize() as:
* transfer_rate = bytes_transferred / (end_time - start_time)
*
* Useful for performance analysis and comparison.
* Typical values: 10 KB/s to 10 MB/s depending on network conditions.
*/
double transfer_rate;
} file_transfer_stats_t;
/*
* ==================================================================================
* CORE FILE MANAGER FUNCTIONS
* ==================================================================================
*/
/*
* file_manager_open_read - Open file for reading in client mode
*
* Opens the specified file for binary reading and initializes the file_manager_t
* structure. This function:
* 1. Validates file exists and is readable
* 2. Opens file with fopen(filename, "rb")
* 3. Determines file size using fseek(SEEK_END) + ftell()
* 4. Reads file permissions and modification time using stat()
* 5. Calculates total_fragments based on file_size and chunk_size
* 6. Initializes tracking fields (bytes_processed=0, current_fragment=0)
*
* VALIDATION:
* - File must exist (checked with access(F_OK))
* - File must be readable (checked with access(R_OK))
* - File must be a regular file (not directory, device, symlink)
* - File size must be > 0 (empty files rejected)
*
* ERROR HANDLING:
* - Returns -1 if file doesn't exist, is not readable, or is empty
* - Returns -1 if fopen() fails (permissions, too many open files, etc.)
* - Sets error context with detailed message
*
* @param fm: File manager structure to initialize (output parameter)
* @param filename: Path to file to read (can be relative or absolute)
*
* @return:
* 0: Success - file opened and ready for reading
* -1: Failure - error details in error context
*
* EXAMPLE:
* file_manager_t fm;
* if (file_manager_open_read(&fm, "/path/to/file.txt") < 0) {
* error_print_last();
* return -1;
* }
* printf("File size: %lu bytes, %u fragments\n", fm.file_size, fm.total_fragments);
*/
int file_manager_open_read(file_manager_t *fm, const char *filename);
/*
* file_manager_open_write - Open file for writing in server mode
*
* Opens the specified file for binary writing and initializes the file_manager_t
* structure. This function:
* 1. Extracts basename from filename (security: prevent path traversal)
* 2. Validates output path is safe (no ../.. components)
* 3. Opens file with fopen(basename, "wb") in current directory
* 4. Initializes write mode tracking fields
*
* SECURITY FEATURES:
* - ALWAYS strips directory components from filename
* * "../../../etc/passwd" → "passwd"
* * "/tmp/evil" → "evil"
* * "subdir/file.txt" → "file.txt"
* - Validates filename doesn't contain path traversal sequences
* - Creates file in current working directory only
* - Uses safe permissions (0644) for new files
*
* OVERWRITE BEHAVIOR:
* - Existing files are OVERWRITTEN without warning
* - Consider using file_exists() check before calling if needed
*
* FILE SIZE:
* - file_size is initially 0 - must be set by file_manager_set_file_size()
* after receiving START packet metadata
*
* @param fm: File manager structure to initialize (output parameter)
* @param filename: Basename of file to write (path components stripped)
*
* @return:
* 0: Success - file opened and ready for writing
* -1: Failure - error details in error context
*
* EXAMPLE (Server):
* file_manager_t fm;
* // metadata->filename might be "../../../evil"
* if (file_manager_open_write(&fm, metadata->filename) < 0) {
* error_print_last();
* return -1;
* }
* // File is safely created as "./evil" in current directory
* file_manager_set_file_size(&fm, metadata->file_size);
*/
int file_manager_open_write(file_manager_t *fm, const char *filename);
/*
* file_manager_read_chunk - Read next chunk of data from file
*
* Reads up to chunk_size bytes from the current file position and advances
* the internal state (bytes_processed, current_fragment).
*
* BEHAVIOR:
* - Reads from current file position (maintained by FILE* stream)
* - Returns actual bytes read (may be less than chunk_size at end of file)
* - Increments bytes_processed by bytes read
* - Increments current_fragment after each read
* - Safe to call repeatedly until file_manager_is_complete() returns true
*
* END OF FILE:
* - Last chunk may be smaller than chunk_size (e.g., 500 bytes instead of 1400)
* - bytes_read parameter contains actual bytes read (0 indicates EOF)
* - Caller should check bytes_read to determine chunk size
*
* ERROR HANDLING:
* - Returns -1 if read_mode is not set (file opened for write)
* - Returns -1 if fread() fails (disk error, file removed, etc.)
* - Returns -1 if file_handle is NULL (file not open)
*
* @param fm: Initialized file manager (must be in read mode)
* @param buffer: Buffer to receive chunk data (min MAX_PAYLOAD_SIZE bytes)
* @param chunk_size: Maximum bytes to read (typically MAX_PAYLOAD_SIZE = 1400)
* @param bytes_read: Output parameter for actual bytes read
*
* @return:
* 0: Success - chunk read successfully
* -1: Failure - error details in error context
*
* EXAMPLE:
* uint8_t chunk[MAX_PAYLOAD_SIZE];
* size_t bytes_read;
* while (file_manager_read_chunk(&fm, chunk, MAX_PAYLOAD_SIZE, &bytes_read) == 0) {
* if (bytes_read == 0) break; // EOF
* send_data_packet(chunk, bytes_read);
* }
*/
int file_manager_read_chunk(file_manager_t *fm, void *buffer, size_t chunk_size, size_t *bytes_read);
/*
* file_manager_write_chunk - Write chunk of data to file
*
* Writes bytes_to_write bytes from buffer to the current file position and
* advances the internal state (bytes_processed, current_fragment).
*
* BEHAVIOR:
* - Writes to current file position (maintained by FILE* stream)
* - Writes exactly bytes_to_write bytes (partial writes considered errors)
* - Increments bytes_processed by bytes written
* - Can be called in any order (out-of-order chunks handled if seeking enabled)
*
* FILE SIZE VALIDATION:
* - Checks that bytes_processed + bytes_to_write <= file_size (if file_size set)
* - Prevents writing more data than expected from metadata
* - Returns error if write would exceed expected file size
*
* ERROR HANDLING:
* - Returns -1 if read_mode is set (file opened for read)
* - Returns -1 if fwrite() fails (disk full, permissions, etc.)
* - Returns -1 if file_handle is NULL (file not open)
* - Returns -1 if write would exceed expected file_size
*
* @param fm: Initialized file manager (must be in write mode)
* @param buffer: Chunk data to write
* @param bytes_to_write: Number of bytes to write
*
* @return:
* 0: Success - chunk written successfully
* -1: Failure - error details in error context
*
* EXAMPLE:
* // After receiving DATA packet with chunk
* if (file_manager_write_chunk(&fm, chunk_data, chunk_size) < 0) {
* error_print_last();
* send_nack_packet(ERR_FILE_ERROR);
* return -1;
* }
*/
int file_manager_write_chunk(file_manager_t *fm, const void *buffer, size_t bytes_to_write);
/*
* file_manager_seek_to_position - Seek to specific byte offset in file
*
* Moves the file position to the specified byte offset using fseek().
* Used for random-access writes when packets arrive out of order.
*
* USAGE:
* Server mode can use this to handle out-of-order fragment reception:
* fragment_offset = fragment_number * chunk_size;
* file_manager_seek_to_position(&fm, fragment_offset);
* file_manager_write_chunk(&fm, chunk_data, chunk_size);
*
* LIMITATIONS:
* - position must be <= file_size
* - Seeking beyond EOF is prevented
*
* @param fm: Initialized file manager
* @param position: Byte offset from start of file (0-based)
*
* @return:
* 0: Success
* -1: Failure - invalid position or fseek() error
*/
int file_manager_seek_to_position(file_manager_t *fm, uint64_t position);
/*
* file_manager_get_fragment_info - Get current and total fragment numbers
*
* Returns fragment tracking information for display or packet header population.
*
* @param fm: File manager instance
* @param current: Output parameter for current_fragment (can be NULL)
* @param total: Output parameter for total_fragments (can be NULL)
*
* @return:
* 0: Success
* -1: Failure - fm is NULL
*/
int file_manager_get_fragment_info(const file_manager_t *fm, uint32_t *current, uint32_t *total);
/*
* file_manager_get_file_size - Get total file size
*
* @param fm: File manager instance
* @return: File size in bytes, or 0 if fm is NULL
*/
uint64_t file_manager_get_file_size(const file_manager_t *fm);
/*
* file_manager_get_bytes_processed - Get bytes processed so far
*
* @param fm: File manager instance
* @return: Bytes read or written, or 0 if fm is NULL
*/
uint64_t file_manager_get_bytes_processed(const file_manager_t *fm);
/*
* file_manager_get_filename - Get filename
*
* @param fm: File manager instance
* @return: Pointer to filename string, or NULL if fm is NULL
*/
const char *file_manager_get_filename(const file_manager_t *fm);
/*
* file_manager_is_complete - Check if transfer is complete
*
* Returns true (1) if bytes_processed >= file_size, false (0) otherwise.
*
* @param fm: File manager instance
* @return: 1 if complete, 0 if not complete or fm is NULL
*/
int file_manager_is_complete(const file_manager_t *fm);
/*
* file_manager_set_file_size - Set expected file size (server mode)
*
* Called after receiving START packet metadata to set the expected file size.
* Also calculates total_fragments based on file_size.
*
* @param fm: File manager instance (must be in write mode)
* @param file_size: Expected file size from metadata
*/
void file_manager_set_file_size(file_manager_t *fm, uint64_t file_size);
/*
* file_manager_get_basename - Extract basename from path
*
* Extracts the filename component from a path, removing directory components.
* Used to sanitize filenames received from network.
*
* Examples:
* - "/path/to/file.txt" → "file.txt"
* - "../../../etc/passwd" → "passwd"
* - "file.txt" → "file.txt"
*
* @param path: Input path (can contain directory components)
* @param basename: Output buffer for basename
* @param basename_len: Size of output buffer
*
* @return:
* 0: Success
* -1: Failure - path is NULL, empty, or basename_len too small
*/
int file_manager_get_basename(const char *path, char *basename, size_t basename_len);
/*
* file_manager_cleanup - Close file and free resources
*
* Releases all resources associated with the file manager:
* 1. Closes file using fclose() (SAFE_CLOSE macro)
* 2. Frees filename string (SAFE_FREE macro)
* 3. Zeros structure fields
*
* SAFE TO CALL MULTIPLE TIMES:
* Uses SAFE_FREE/SAFE_CLOSE which set pointers to NULL after cleanup.
*
* MUST BE CALLED:
* Failure to call this function leaks a FILE* and memory for filename.
*
* @param fm: File manager to clean up
*/
void file_manager_cleanup(file_manager_t *fm);
/*
* ==================================================================================
* FILE UTILITY FUNCTIONS
* ==================================================================================
*/
/*
* file_exists - Check if file exists
* @return: 1 if exists, 0 if not exists or error
*/
int file_exists(const char *filename);
/*
* file_is_readable - Check if file is readable
* @return: 1 if readable, 0 if not readable or error
*/
int file_is_readable(const char *filename);
/*
* file_is_writable - Check if file is writable
* @return: 1 if writable, 0 if not writable or error
*/
int file_is_writable(const char *filename);
/*
* file_get_size - Get file size using stat()
* @return: File size in bytes, or 0 on error
*/
uint64_t file_get_size(const char *filename);
/*
* file_get_permissions - Get file permissions
* @return: 0 on success, -1 on error
*/
int file_get_permissions(const char *filename, mode_t *permissions);
/*
* file_set_permissions - Set file permissions
* @return: 0 on success, -1 on error
*/
int file_set_permissions(const char *filename, mode_t permissions);
/*
* file_get_modification_time - Get last modification time
* @return: Unix timestamp, or 0 on error
*/
time_t file_get_modification_time(const char *filename);
/*
* ==================================================================================
* SAFE FILE OPERATIONS
* ==================================================================================
*/
/*
* file_create_temp - Create temporary file with unique name
*
* Creates a temporary file using mkstemp() with the provided template.
* Template must end with "XXXXXX" which will be replaced with unique suffix.
*
* @param template_path: Template path (e.g., "file_XXXXXX"), modified in place
* @param template_size: Size of template_path buffer
* @return: File descriptor of created temp file, or -1 on error
*/
int file_create_temp(char *template_path, size_t template_size);
/*
* file_atomic_rename - Atomically rename temporary file to final name
*
* Uses rename() which is atomic on POSIX systems. If final file exists,
* it is atomically replaced.
*
* @param temp_filename: Temporary file path
* @param final_filename: Final file path
* @return: 0 on success, -1 on error
*/
int file_atomic_rename(const char *temp_filename, const char *final_filename);
/*
* file_secure_delete - Securely delete file with overwrite
*
* Overwrites file contents with zeros before unlinking to prevent recovery.
* Note: May not be effective on SSD/flash storage due to wear leveling.
*
* @param filename: File to securely delete
* @return: 0 on success, -1 on error
*/
int file_secure_delete(const char *filename);
/*
* file_create_backup - Create backup copy of file
*
* Copies filename to filename.bak or filename.bak.N if backup exists.
*
* @param filename: File to backup
* @param backup_filename: Output buffer for backup filename
* @param backup_size: Size of backup_filename buffer
* @return: 0 on success, -1 on error
*/
int file_create_backup(const char *filename, char *backup_filename, size_t backup_size);
/*
* ==================================================================================
* PATH MANIPULATION FUNCTIONS
* ==================================================================================
*/
/*
* path_normalize - Normalize path by resolving . and .. components
* @return: 0 on success, -1 on error
*/
int path_normalize(const char *input_path, char *output_path, size_t output_size);
/*
* path_join - Join directory and filename components
* @return: 0 on success, -1 on error
*/
int path_join(const char *dir, const char *filename, char *output_path, size_t output_size);
/*
* path_get_directory - Extract directory component from filepath
* @return: 0 on success, -1 on error
*/
int path_get_directory(const char *filepath, char *directory, size_t directory_size);
/*
* path_is_absolute - Check if path is absolute (starts with /)
* @return: 1 if absolute, 0 if relative
*/
int path_is_absolute(const char *path);
/*
* path_is_safe - Check if path is safe (no .. traversal)
*
* Validates path doesn't contain:
* - ".." components (parent directory traversal)
* - Null bytes
* - Excessively long components
*
* @return: 1 if safe, 0 if unsafe
*/
int path_is_safe(const char *path);
/*
* ==================================================================================
* TRANSFER STATISTICS FUNCTIONS
* ==================================================================================
*/
/*
* file_transfer_stats_init - Initialize transfer statistics
* Sets start_time to current time, zeros other fields.
*/
void file_transfer_stats_init(file_transfer_stats_t *stats);
/*
* file_transfer_stats_update - Update bytes transferred
* Increments bytes_transferred and appropriate fragment counter.
*/
void file_transfer_stats_update(file_transfer_stats_t *stats, uint64_t bytes_transferred);
/*
* file_transfer_stats_finalize - Finalize statistics
* Sets end_time and calculates transfer_rate.
*/
void file_transfer_stats_finalize(file_transfer_stats_t *stats);
/*
* file_transfer_stats_print - Print statistics summary
* Displays total bytes, duration, transfer rate, etc.
*/
void file_transfer_stats_print(const file_transfer_stats_t *stats);
/*
* file_transfer_stats_get_rate - Get transfer rate
* @return: Transfer rate in bytes/second
*/
double file_transfer_stats_get_rate(const file_transfer_stats_t *stats);
/*
* file_transfer_stats_get_progress - Get progress percentage
* @return: Progress as percentage (0.0 to 100.0)
*/
double file_transfer_stats_get_progress(const file_transfer_stats_t *stats);
#endif /* FILE_IO_H */