830 lines
28 KiB
C
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 */
|