Compare commits

..

No commits in common. "22cb958b4f88623a15c4d201cc8476a39e50dbc4" and "77775e409762c8a317f9e0fe72d6af9399fd921a" have entirely different histories.

14 changed files with 301 additions and 603 deletions

View File

@ -48,12 +48,6 @@
int auto_create_perm = GRANT_TEMP; int auto_create_perm = GRANT_TEMP;
/*
* Sets the default permission granted by file creation.
*
* @param val: the default permission to grant. Should be one of GRANT_TEMP,
* GRANT_TEM and 0. When set to 0, grants no permissions.
*/
void set_auto_create_perm(int val) { auto_create_perm = val; } void set_auto_create_perm(int val) { auto_create_perm = val; }
static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg) { static void *xmp_init(struct fuse_conn_info *conn, struct fuse_config *cfg) {
@ -403,8 +397,8 @@ static int xmp_chmod(const char *path, mode_t mode, struct fuse_file_info *fi) {
} }
/** /**
* This filesystem is not designed for multiuser operation (with * This filesystem is not designed for multiuser operation (e.g. with
* allow_other option) so there is little point in having chown implemnted * allow_other) so there is little point in having chown implemnted
*/ */
static int xmp_chown(const char *path, uid_t uid, gid_t gid, static int xmp_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi) { struct fuse_file_info *fi) {
@ -472,6 +466,8 @@ static int xmp_create(const char *path, mode_t mode,
pi = get_process_info(fc->pid); pi = get_process_info(fc->pid);
// fprintf(stderr, "%s, %d\n", path, ask_access(path, pi));
if (auto_create_perm != 0) { if (auto_create_perm != 0) {
if (!interactive_access(path, pi, auto_create_perm)) { if (!interactive_access(path, pi, auto_create_perm)) {
free(pi.name); free(pi.name);

View File

@ -14,13 +14,6 @@
#include <fuse3/fuse.h> #include <fuse3/fuse.h>
const struct fuse_operations *get_fuse_operations(); const struct fuse_operations *get_fuse_operations();
/*
* Sets the default permission granted by file creation.
*
* @param val: the default permission to grant. Should be one of GRANT_TEMP,
* GRANT_TEM and 0. When set to 0, grants no permissions.
*/
void set_auto_create_perm(int val); void set_auto_create_perm(int val);
#endif #endif

View File

@ -23,27 +23,45 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
// Global pointer to the SQLite database storing permanent permissions
sqlite3 *perm_database = NULL; sqlite3 *perm_database = NULL;
const char *const table_name = "permissions"; const char *const table_name = "permissions";
// Each row represents a permission to access a specific file for an executable // one row corresponds to a permission to access one file for one executable
const int column_count = 3; const int column_count = 3;
// Column names in the permissions table
const char *const schema[] = {"executable", "filename", "mode"}; const char *const schema[] = {"executable", "filename", "mode"};
// Expected SQL data types for each column
const char *const types[] = {"TEXT", "TEXT", "INTEGER"}; const char *const types[] = {"TEXT", "TEXT", "INTEGER"};
uid_t ruid, euid, current_uid; uid_t ruid, euid, current_uid;
pthread_mutex_t uid_switch = PTHREAD_MUTEX_INITIALIZER;
void set_db_fsuid() {
pthread_mutex_lock(&uid_switch);
if (current_uid == ruid)
return;
int status = -1;
status = setfsuid(ruid);
if (status < 0) {
fprintf(stderr, "[ICFS] Couldn't set uid to %d.\n", ruid);
exit(status);
}
pthread_mutex_unlock(&uid_switch);
}
void set_real_fsuid() {
pthread_mutex_lock(&uid_switch);
if (current_uid == ruid)
return;
int status = -1;
status = setfsuid(ruid);
if (status < 0) {
fprintf(stderr, "[ICFS] Couldn't set uid to %d.\n", euid);
exit(status);
}
pthread_mutex_unlock(&uid_switch);
}
/**
* Callback to validate the database schema matches expectations.
* Verifies column order, names, and types during schema checks.
*
* @param unused: Ignored context pointer
* @param argc: Number of columns returned (should be 3)
* @param argv: Column data: index 0=name, 1=type, 2=notnull flag
* @param colname: Column names (unused)
* @return 0 on success, 1 if schema mismatch
*/
static int check_table_col_schema(void *notused, int argc, char **argv, static int check_table_col_schema(void *notused, int argc, char **argv,
char **colname) { char **colname) {
(void)notused; (void)notused;
@ -68,13 +86,27 @@ static int check_table_col_schema(void *notused, int argc, char **argv,
return 1; return 1;
} }
/** static int set_flag(void *flag, int argc, char **argv, char **colname) {
* Creates the permissions table schema in the database. (void)colname;
* Called when the table doesn't exist yet.
* if (argc < 3) {
* @return 0 on success, 1 on failure fprintf(
*/ stderr,
int create_database_schema(void) { "[ICFS] Unexpected amount of arguments given to the callback: %d.\n",
argc);
return 1;
}
if (atoi(argv[2])) {
fprintf(stderr, "[ICFS] Third column was: %s\n", argv[2]);
*(int *)flag = 1;
} else {
*(int *)flag = -1;
}
return 0;
}
int create_database_schema() {
fprintf(stderr, "[ICFS] Creating table 'permissions'.\n"); fprintf(stderr, "[ICFS] Creating table 'permissions'.\n");
const char *create_query = const char *create_query =
"CREATE TABLE permissions(executable TEXT NOT " "CREATE TABLE permissions(executable TEXT NOT "
@ -93,12 +125,11 @@ int create_database_schema(void) {
} }
/** /**
* Ensures the database schema matches expected structure. * Ensures that the database schema is correct.
* Validates table existence and column definitions.
* *
* @return 0 if schema is valid, 1 if validation failed or repair failed * @return: 0 if the schema is correct, 1 if the schema could not be corrected.
*/ */
int ensure_database_schema(void) { int ensure_database_schema() {
// Check for the table. // Check for the table.
int result = sqlite3_table_column_metadata( int result = sqlite3_table_column_metadata(
perm_database, NULL, table_name, NULL, NULL, NULL, NULL, NULL, NULL); perm_database, NULL, table_name, NULL, NULL, NULL, NULL, NULL, NULL);
@ -114,7 +145,6 @@ int ensure_database_schema(void) {
return 1; return 1;
} }
// Verify column definitions
const char *pragma = "PRAGMA table_info(permissions);"; const char *pragma = "PRAGMA table_info(permissions);";
char *err = NULL; char *err = NULL;
int ret = int ret =
@ -131,20 +161,18 @@ int ensure_database_schema(void) {
} }
/** /**
* Initializes the permanent permissions database. * Initializes the permanent permissions table.
* Creates/opens the database file and sets up schema.
* *
* @param db_filename Path to the SQLite database file * @param db_filename: The filename of the permissions sqlite3 database
* @return 0 on success, -1 on failure * @return: 0 on success, -1 on failure
*/ */
int init_perm_permissions_table(const char *db_filename) { int init_perm_permissions_table(const char *db_filename) {
// Prevent group/others access during file creation // we don't want the group and others to access the db
umask(0077); umask(0077);
ruid = getuid(); ruid = getuid();
euid = geteuid(); euid = geteuid();
fprintf(stderr, "[ICFS] Running with uid: %d, gid: %d\n", euid, getegid()); fprintf(stderr, "[ICFS] Running with uid: %d, gid: %d\n", euid, getegid());
// Open database with read/write access and full mutex protection
if (sqlite3_open_v2(db_filename, &perm_database, if (sqlite3_open_v2(db_filename, &perm_database,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
SQLITE_OPEN_FULLMUTEX, SQLITE_OPEN_FULLMUTEX,
@ -152,16 +180,12 @@ int init_perm_permissions_table(const char *db_filename) {
perror("[ICFS] Can't open permanent permissions database"); perror("[ICFS] Can't open permanent permissions database");
return -1; return -1;
} }
umask(0); // Restore default umask umask(0);
// Verify and initialize schema
if (ensure_database_schema()) { if (ensure_database_schema()) {
fprintf(stderr, "[ICFS] Database schema is not correct.\n"); fprintf(stderr, "[ICFS] Database schema is not correct.\n");
return -1; return -1;
} }
// Switch to real UID, since we started with the icfs user to open the
// database
int status = seteuid(ruid); int status = seteuid(ruid);
if (status < 0) { if (status < 0) {
fprintf(stderr, fprintf(stderr,
@ -173,18 +197,17 @@ int init_perm_permissions_table(const char *db_filename) {
} }
/** /**
* Closes the database connection and releases resources. * Destroys the permanent permissions table.
* Should be called when the module is shutting down.
*/ */
void destroy_perm_permissions_table(void) { sqlite3_close(perm_database); } void destroy_perm_permissions_table(void) { sqlite3_close(perm_database); }
/** /**
* Checks if a specific process has permanent access to a file. * Checks if the process has a permanent access to the file.
* Does not check parent processes.
* *
* @param filename Path to the file being accessed * @param filename: The file that the process is trying to access
* @param pi Process information structure * @param pi: The process information
* @return ALLOW (explicit allow), DENY (explicit deny), or NDEF (no info) * @return: access status - ALLOW, DENY or NDEF in case if no information was
* found
*/ */
access_t check_perm_access_noparent(const char *filename, access_t check_perm_access_noparent(const char *filename,
struct process_info pi) { struct process_info pi) {
@ -193,16 +216,10 @@ access_t check_perm_access_noparent(const char *filename,
access_t ret = NDEF; access_t ret = NDEF;
sqlite3_stmt *stmt = NULL; sqlite3_stmt *stmt = NULL;
// Query checks:
// 1. Exact match for filename
// 2. Directory prefix match (filename starts with stored path + '/')
// Ordered by longest path for most specific match first
const char *sql = const char *sql =
"SELECT mode FROM permissions WHERE executable = ?1 " "SELECT mode FROM permissions WHERE executable = ?1 "
"AND (( ?2 LIKE (filename || \'%\') AND filename " "AND (( ?2 LIKE (filename || \'%\') AND filename "
"GLOB \'*/\') OR filename = ?2 ) ORDER BY LENGTH( filename ) DESC;"; "GLOB \'*/\') OR filename = ?2 ) ORDER BY LENGTH( filename ) DESC;";
sqlite3_prepare_v2(perm_database, sql, -1, &stmt, NULL); sqlite3_prepare_v2(perm_database, sql, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, pi.name, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 1, pi.name, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, filename, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, filename, -1, SQLITE_STATIC);
@ -213,24 +230,30 @@ access_t check_perm_access_noparent(const char *filename,
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return ret; return ret;
} }
if (step_ret == SQLITE_ROW) { if (step_ret == SQLITE_ROW) {
int mode_col = sqlite3_column_int(stmt, 0); int mode_col = sqlite3_column_int(stmt, 0);
ret = mode_col ? ALLOW : DENY; if (mode_col) {
ret = ALLOW;
} else {
ret = DENY;
}
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return ret; return ret;
} }
/** /**
* Checks if a process or any of its ancestors have permanent access. * Checks if the process or any of it's parents have permanent access to the
* Handles hierarchical permission inheritance. * file.
* *
* @param filename Path to the file being accessed * @param filename: The file that the process is trying to access
* @param pi Process information structure * @param pi: The process information
* @return ALLOW/DENY/NDEF with NDEF meaning no explicit rule found * @return: access status - ALLOW, DENY or NDEF in case if no information was
* @note May return false negatives if parent processes terminate during check * found. Does not return ALLOW_TEMP or DENY_TEMP.
* @note: In case one of the parent processes is killed while this function
* execution the result is not guranteed to be correct. It should only lead to
* false negatives, though.
*/ */
access_t check_perm_access(const char *filename, struct process_info pi) { access_t check_perm_access(const char *filename, struct process_info pi) {
if (pi.PID == 0 || pi.name == NULL) { if (pi.PID == 0 || pi.name == NULL) {
@ -239,17 +262,13 @@ access_t check_perm_access(const char *filename, struct process_info pi) {
struct process_info current_pi = pi; struct process_info current_pi = pi;
current_pi.name = strdup(current_pi.name); current_pi.name = strdup(current_pi.name);
while (current_pi.PID != 0) { while (current_pi.PID != 0) {
access_t access = check_perm_access_noparent(filename, current_pi); access_t access = check_perm_access_noparent(filename, current_pi);
free(current_pi.name); free(current_pi.name);
if (access != NDEF) { if (access != NDEF) {
return access; return access;
} }
current_pi.name = NULL; current_pi.name = NULL;
// Traverse to parent process
while (current_pi.name == NULL) { while (current_pi.name == NULL) {
current_pi.PID = get_main_thread_pid(get_parent_pid(current_pi.PID)); current_pi.PID = get_main_thread_pid(get_parent_pid(current_pi.PID));
if (current_pi.PID != 0) { if (current_pi.PID != 0) {
@ -264,13 +283,11 @@ access_t check_perm_access(const char *filename, struct process_info pi) {
} }
/** /**
* Sets a permanent access rule for a process-file combination. * Gives permanent access to the process to the file.
* *
* @param filename Path to the file needing access * @param filename: The file that the process is trying to access
* @param pi Process information * @param pi: The process information
* @param mode Kind of access rule to be set - SET_DENY to deny access, and * @return: 0 on success, 1 on failure
* SET_ALLOW to allow access.
* @return 0 on success, 1 on failure
*/ */
int set_perm_access(const char *filename, struct process_info pi, int set_perm_access(const char *filename, struct process_info pi,
set_mode_t mode) { set_mode_t mode) {
@ -282,20 +299,19 @@ int set_perm_access(const char *filename, struct process_info pi,
} else if (mode == SET_DENY) { } else if (mode == SET_DENY) {
sql = "INSERT INTO permissions VALUES (?1, ?2, FALSE);"; sql = "INSERT INTO permissions VALUES (?1, ?2, FALSE);";
} else { } else {
return 1; // Invalid mode return 1;
} }
sqlite3_prepare_v2(perm_database, sql, -1, &stmt, NULL); sqlite3_prepare_v2(perm_database, sql, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, pi.name, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 1, pi.name, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, filename, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, filename, -1, SQLITE_STATIC);
int step_ret = sqlite3_step(stmt); int step_ret = sqlite3_step(stmt);
if (step_ret != SQLITE_DONE) { if (step_ret != SQLITE_DONE) {
fprintf(stderr, "[ICFS] SQLite error: %s\n", sqlite3_errstr(step_ret)); fprintf(stderr, "[ICFS] SQLite error: %s\n", sqlite3_errstr(step_ret));
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return 1; return 1;
} }
sqlite3_finalize(stmt); sqlite3_finalize(stmt);
return 0; return 0;
} }

View File

@ -14,38 +14,36 @@
#include "set_mode_t.h" #include "set_mode_t.h"
/** /**
* Initializes the permanent permissions database. * Initializes the permanent permissions table.
* Creates/opens the database file and sets up schema.
* *
* @param db_filename Path to the SQLite database file * @param db_filename: The filename of the permissions sqlite3 database
* @return 0 on success, -1 on failure * @return: 0 on success, -1 on failure (e.g. ENOMEM)
*/ */
int init_perm_permissions_table(const char *db_filename); int init_perm_permissions_table(const char *db_filename);
/** /**
* Closes the database connection and releases resources. * Destroys the permanent permissions table.
* Should be called when the module is shutting down.
*/ */
void destroy_perm_permissions_table(); void destroy_perm_permissions_table();
/** /**
* Checks if a process or any of its ancestors have permanent access. * Checks if the process has a permanent access to the file.
* Handles hierarchical permission inheritance.
* *
* @param filename Path to the file being accessed * @param filename: The file that the process is trying to access
* @param pi Process information structure * @param pi: The process information
* @return ALLOW/DENY/NDEF with NDEF meaning no explicit rule found * @return: access status - ALLOW, DENY or NDEF in case if no information was
* @note May return false negatives if parent processes terminate during check * found
*/ */
access_t check_perm_access(const char *filename, struct process_info pi); access_t check_perm_access(const char *filename, struct process_info pi);
/** /**
* Sets a permanent access rule for a process-file combination. * Gives permanent access to the process to the file.
* *
* @param filename Path to the file needing access * @param filename: The file that the process is trying to access
* @param pi Process information * @param pi: The process information
* @param mode SET_ALLOW (whitelist) or SET_DENY (blacklist) * @param mode: Kind of access rule to be set - SET_DENY to deny access, and
* @return 0 on success, 1 on failure * SET_ALLOW to allow access.
* @return: 0 on success, 1 on failure
*/ */
int set_perm_access(const char *filename, struct process_info pi, int set_perm_access(const char *filename, struct process_info pi,
set_mode_t mode); set_mode_t mode);

View File

@ -5,6 +5,7 @@
This program can be distributed under the terms of the GNU GPLv2. This program can be distributed under the terms of the GNU GPLv2.
See the file LICENSE. See the file LICENSE.
*/ */
#include "proc_operations.h" #include "proc_operations.h"
#include <linux/limits.h> #include <linux/limits.h>
#include <stddef.h> #include <stddef.h>
@ -16,19 +17,13 @@
* @brief Returns the PID of the main thread (i.e., the process ID) of the * @brief Returns the PID of the main thread (i.e., the process ID) of the
* process that the given thread ID (tid) belongs to. * process that the given thread ID (tid) belongs to.
* *
* In Linux, threads within the same process share the same thread group ID
* (TGID), which is equal to the PID of the main thread. This function retrieves
* the TGID by parsing `/proc/<tid>/status`, effectively returning the main
* thread's PID.
*
* @param tid The thread ID (TID) of any thread in the process. * @param tid The thread ID (TID) of any thread in the process.
* @return pid_t The process ID (main thread's PID), or 0 on error (invalid tid * @return pid_t The process ID (main thread's PID), or -1 on error.
* or file read failure).
*/ */
pid_t get_main_thread_pid(pid_t tid) { pid_t get_main_thread_pid(pid_t tid) {
// Validate input // Validate input
if (tid <= 0) { if (tid <= 0) {
// Invalid TID: TIDs are always positive in Linux // errno = EINVAL;
return 0; return 0;
} }
@ -50,31 +45,14 @@ pid_t get_main_thread_pid(pid_t tid) {
} }
fclose(fp); fclose(fp);
// Debugging check: If the provided tid is not the main thread's tid,
// this logs a message but does not affect the return value.
// This is useful for detecting cases where non-main threads are being
// inspected.
if (tgid != tid) { if (tgid != tid) {
fprintf(stderr, fprintf(stderr,
"[ICFS] The tid and and pid wasn't equal. tid:%d, pid:%d.\n", tid, "[ICFS] The tid and and pid wasn't equal. tid:%d, pid:%d.\n", tid,
tgid); tgid);
} }
return tgid; return tgid;
} }
/**
* @brief Retrieves the full path of the executable for a given process ID.
*
* This function reads the `/proc/<pid>/exe` symbolic link, which points to the
* executable file of the process. The returned string is dynamically allocated
* and must be freed by the caller.
*
* @param pid The process ID to query.
* @return char* Dynamically allocated string containing the executable path, or
* NULL on failure.
*/
char *get_process_name_by_pid(const int pid) { char *get_process_name_by_pid(const int pid) {
char path[1024]; char path[1024];
sprintf(path, "/proc/%d/exe", pid); sprintf(path, "/proc/%d/exe", pid);
@ -96,8 +74,6 @@ char *get_process_name_by_pid(const int pid) {
free(name); free(name);
return NULL; return NULL;
} }
// If the buffer was too small, double its size and try again
if ((size_t)len >= size) { if ((size_t)len >= size) {
size *= 2; size *= 2;
char *new_name = realloc(name, size); char *new_name = realloc(name, size);
@ -107,8 +83,8 @@ char *get_process_name_by_pid(const int pid) {
} }
name = new_name; name = new_name;
} else { } else {
// readlink does not null-terminate, so we must do it manually // readlink does not set the null character
name[len] = '\0'; name[len] = 0;
break; break;
} }
} }
@ -117,21 +93,16 @@ char *get_process_name_by_pid(const int pid) {
} }
/** /**
* @brief Finds the parent process ID of a given process. * Finds the parent process ID of a given process.
* *
* This function parses the `/proc/<pid>/status` file to extract the `PPid` * @param pid: The process ID of the process to find the parent of
* field, which represents the parent process ID. This is useful for tracing * @return: The parent process ID, or 0 if the parent process ID could not be
* process lineage. * found
*
* @param pid The process ID of the process to find the parent of.
* @return pid_t The parent process ID, or 0 if the parent could not be
* determined.
*/ */
pid_t get_parent_pid(pid_t pid) { pid_t get_parent_pid(pid_t pid) {
pid_t ppid = 0; pid_t ppid = 0;
char path[256]; char path[256];
snprintf(path, sizeof(path), "/proc/%d/status", snprintf(path, sizeof(path), "/proc/%u/status", pid);
pid); // Use %d for signed pid_t
FILE *file = fopen(path, "r"); FILE *file = fopen(path, "r");
if (file == NULL) { if (file == NULL) {
@ -141,7 +112,6 @@ pid_t get_parent_pid(pid_t pid) {
char line[256]; char line[256];
while (fgets(line, sizeof(line), file)) { while (fgets(line, sizeof(line), file)) {
// The "PPid:" field in `/proc/<pid>/status` is followed by a tab character
if (sscanf(line, "PPid:\t%d", &ppid) == 1) { if (sscanf(line, "PPid:\t%d", &ppid) == 1) {
fclose(file); fclose(file);
return ppid; return ppid;

View File

@ -11,29 +11,14 @@
#include <time.h> #include <time.h>
/**
* @brief Retrieves the full path of the executable for a given process ID.
*
* This function reads the `/proc/<pid>/exe` symbolic link, which points to the
* executable file of the process. The returned string is dynamically allocated
* and must be freed by the caller.
*
* @param pid The process ID to query.
* @return char* Dynamically allocated string containing the executable path, or
* NULL on failure.
*/
char *get_process_name_by_pid(const int pid); char *get_process_name_by_pid(const int pid);
/** /**
* @brief Finds the parent process ID of a given process. * Finds the parent process ID of a given process.
* *
* This function parses the `/proc/<pid>/status` file to extract the `PPid` * @param pid: The process ID of the process to find the parent of
* field, which represents the parent process ID. This is useful for tracing * @return: The parent process ID, or 0 if the parent process ID could not be
* process lineage. * found
*
* @param pid The process ID of the process to find the parent of.
* @return pid_t The parent process ID, or 0 if the parent could not be
* determined.
*/ */
pid_t get_parent_pid(pid_t pid); pid_t get_parent_pid(pid_t pid);
@ -41,14 +26,8 @@ pid_t get_parent_pid(pid_t pid);
* @brief Returns the PID of the main thread (i.e., the process ID) of the * @brief Returns the PID of the main thread (i.e., the process ID) of the
* process that the given thread ID (tid) belongs to. * process that the given thread ID (tid) belongs to.
* *
* In Linux, threads within the same process share the same thread group ID
* (TGID), which is equal to the PID of the main thread. This function retrieves
* the TGID by parsing `/proc/<tid>/status`, effectively returning the main
* thread's PID.
*
* @param tid The thread ID (TID) of any thread in the process. * @param tid The thread ID (TID) of any thread in the process.
* @return pid_t The process ID (main thread's PID), or 0 on error (invalid tid * @return pid_t The process ID (main thread's PID), or -1 on error.
* or file read failure).
*/ */
pid_t get_main_thread_pid(pid_t tid); pid_t get_main_thread_pid(pid_t tid);

View File

@ -9,18 +9,7 @@
#ifndef REAL_FILENAME_H #ifndef REAL_FILENAME_H
#define REAL_FILENAME_H #define REAL_FILENAME_H
/**
* @brief Build a real path to the file.
*
* @param filename Relative path within the filesystem
* @return Newly allocated absolute path (caller must free)
*/
const char *real_filename(const char *filename); const char *real_filename(const char *filename);
/**
* @brief Get the current mount point path
* @return Absolute path to the filesystem root
*/
const char *get_mountpoint(void); const char *get_mountpoint(void);
#endif // !REAL_FILENAME_H #endif // !REAL_FILENAME_H

View File

@ -16,45 +16,20 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
/**
* @brief Global handle for the source filesystem implementation
*
* Stores the root directory information to enable relative path operations.
*/
static struct source_files_handle { static struct source_files_handle {
const char *mountpoint; // Absolute path to the mounted filesystem root const char *mountpoint;
int root_fd; // File descriptor for the root directory (O_PATH) int root_fd;
} handle; } handle;
/**
* @brief Translate FUSE paths to real paths in the underlying filesystem
*
* FUSE passes paths starting with '/' (e.g., "/dir/file"), but we need to
* operate relative to our root_fd. This function converts:
* - "/" -> "." (current directory)
* - "/dir/file" -> "dir/file"
*
* @param filename Path from FUSE (always starts with '/')
* @return Relative path suitable for use with root_fd
*/
const char *source_filename_translate(const char *filename) { const char *source_filename_translate(const char *filename) {
if (strcmp("/", filename) == 0) { if (strcmp("/", filename) == 0) {
return "."; return ".";
} else {
return filename + 1;
} }
return filename + 1; // Skip leading slash
} }
/**
* @brief Initialize the source filesystem with a root directory
*
* Sets up the mount point and opens a protected file descriptor to the
* root directory for safe relative operations.
*
* @param root_path Absolute path to the physical root directory
* @return 0 on success, -1 on failure
*/
int source_init(const char *root_path) { int source_init(const char *root_path) {
// Allocate memory for mount point path
handle.mountpoint = malloc(strlen(root_path) + 1); handle.mountpoint = malloc(strlen(root_path) + 1);
if (handle.mountpoint == NULL) { if (handle.mountpoint == NULL) {
perror("[ICFS] Malloc failed"); perror("[ICFS] Malloc failed");
@ -63,9 +38,8 @@ int source_init(const char *root_path) {
strcpy(handle.mountpoint, root_path); strcpy(handle.mountpoint, root_path);
// Open root directory with O_PATH to prevent accidental reads/writes
// while maintaining a valid descriptor for relative operations
int root_fd = open(root_path, O_PATH); int root_fd = open(root_path, O_PATH);
if (root_fd == -1) { if (root_fd == -1) {
fprintf(stderr, "[ICFS] Could not initialize source file system at %s", fprintf(stderr, "[ICFS] Could not initialize source file system at %s",
root_path); root_path);
@ -74,115 +48,66 @@ int source_init(const char *root_path) {
} }
handle.root_fd = root_fd; handle.root_fd = root_fd;
return 0; return 0;
} }
/** void source_destroy(void) { free(handle.mountpoint); }
* @brief Clean up resources used by the source filesystem
*/
void source_destroy(void) {
free(handle.mountpoint); // Free allocated mount point path
}
/**
* @brief Get the current mount point path
* @return Absolute path to the filesystem root
*/
const char *get_mountpoint(void) { return handle.mountpoint; } const char *get_mountpoint(void) { return handle.mountpoint; }
/**
* @brief Build a real path to the file.
*
* @param filename Relative path within the filesystem
* @return Newly allocated absolute path (caller must free)
*/
const char *real_filename(const char *filename) { const char *real_filename(const char *filename) {
const char *mountpoint = get_mountpoint(); const char *mountpoint = get_mountpoint();
// Calculate required length
size_t len1 = strlen(mountpoint); size_t len1 = strlen(mountpoint);
size_t len2 = strlen(filename); size_t len2 = strlen(filename);
size_t total_len = len1 + len2;
// Allocate space for combined path + null terminator // Allocate memory (+1 for null terminator)
char *result = malloc(len1 + len2 + 1); char *result = malloc(total_len + 1);
if (result == NULL) { if (result == NULL) {
fprintf(stderr, "[ICFS] Memory allocation failed"); fprintf(stderr, "[ICFS] Memory allocation failed");
perror(""); perror("");
return NULL; return NULL;
} }
// Copy strings
strcpy(result, mountpoint); strcpy(result, mountpoint);
strcat(result, filename); strcat(result, filename);
return result; return result;
} }
/**
* @brief Create a directory in the filesystem
*
* Uses mkdirat() to safely create directories relative to root_fd,
* preventing race conditions from concurrent path modifications.
*/
int source_mkdir(const char *filename, mode_t mode) { int source_mkdir(const char *filename, mode_t mode) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
return mkdirat(handle.root_fd, relative_filename, mode); return mkdirat(handle.root_fd, relative_filename, mode);
} }
/**
* @brief Remove a file from the filesystem
*
* Uses unlinkat() with AT_REMOVEDIR flag to safely remove files relative
* to the root directory.
*/
int source_unlink(const char *filename) { int source_unlink(const char *filename) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
return unlinkat(handle.root_fd, relative_filename, 0); return unlinkat(handle.root_fd, relative_filename, 0);
} }
/**
* @brief Get file status information
*
* Uses fstatat() to retrieve metadata relative to root_fd. Follows symlinks
* by default (flags=0).
*/
int source_stat(const char *restrict filename, struct stat *restrict statbuf) { int source_stat(const char *restrict filename, struct stat *restrict statbuf) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
return fstatat(handle.root_fd, relative_filename, statbuf, 0); return fstatat(handle.root_fd, relative_filename, statbuf, 0);
} }
/**
* @brief Remove an empty directory
*
* Uses unlinkat() with AT_REMOVEDIR flag to safely remove directories.
*/
int source_rmdir(const char *filename) { int source_rmdir(const char *filename) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
return unlinkat(handle.root_fd, relative_filename, AT_REMOVEDIR); return unlinkat(handle.root_fd, relative_filename, AT_REMOVEDIR);
} }
/**
* @brief Create a symbolic link
*
* Creates symlinks relative to the root_fd directory for safety.
*/
int source_symlink(const char *target, const char *linkpath) { int source_symlink(const char *target, const char *linkpath) {
const char *relative_linkpath = source_filename_translate(linkpath); const char *relative_linkpath = source_filename_translate(linkpath);
return symlinkat(target, handle.root_fd, relative_linkpath); return symlinkat(target, handle.root_fd, relative_linkpath);
} }
/**
* @brief Check file access permissions
*
* Uses faccessat() to check access rights relative to root_fd.
*/
int source_access(const char *filename, int mode) { int source_access(const char *filename, int mode) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
return faccessat(handle.root_fd, relative_filename, mode, 0); return faccessat(handle.root_fd, relative_filename, mode, 0);
} }
/**
* @brief Open a directory for reading
*
* Combines openat() and fdopendir() to safely access directories relative
* to the root_fd.
*/
DIR *source_opendir(const char *filename) { DIR *source_opendir(const char *filename) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
int fd = openat(handle.root_fd, relative_filename, 0); int fd = openat(handle.root_fd, relative_filename, 0);
@ -194,11 +119,6 @@ DIR *source_opendir(const char *filename) {
return dir_pointer; return dir_pointer;
} }
/**
* @brief Rename a file or directory
*
* Uses renameat() to safely rename within the same root_fd namespace.
*/
int source_rename(const char *oldpath, const char *newpath) { int source_rename(const char *oldpath, const char *newpath) {
const char *relative_oldpath = source_filename_translate(oldpath); const char *relative_oldpath = source_filename_translate(oldpath);
const char *relative_newpath = source_filename_translate(newpath); const char *relative_newpath = source_filename_translate(newpath);
@ -206,48 +126,25 @@ int source_rename(const char *oldpath, const char *newpath) {
relative_newpath); relative_newpath);
} }
/**
* @brief Create a hard link
*
* Uses linkat() with flags=0 (default behavior). May need AT_SYMLINK_NOFOLLOW
* if symlink handling should be modified.
*/
int source_link(const char *oldpath, const char *newpath) { int source_link(const char *oldpath, const char *newpath) {
const char *relative_oldpath = source_filename_translate(oldpath); const char *relative_oldpath = source_filename_translate(oldpath);
const char *relative_newpath = source_filename_translate(newpath); const char *relative_newpath = source_filename_translate(newpath);
return linkat(handle.root_fd, relative_oldpath, handle.root_fd, return linkat(handle.root_fd, relative_oldpath, handle.root_fd,
relative_newpath, 0); relative_newpath, 0);
// NOTE: perhaps the flags here need to be reevaluated.
} }
/**
* @brief Change file access mode
*
* Uses fchmodat() with flags=0 (follow symlinks). Consider using
* AT_SYMLINK_NOFOLLOW if symlink metadata should be modified directly.
*/
int source_chmod(const char *filename, mode_t mode) { int source_chmod(const char *filename, mode_t mode) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
return fchmodat(handle.root_fd, relative_filename, mode, 0); return fchmodat(handle.root_fd, relative_filename, mode, 0);
// NOTE: perhaps the flags here need to be reevaluated.
} }
/**
* @brief Change file owner and group
*
* Uses fchownat() with AT_SYMLINK_NOFOLLOW to modify symlink metadata
* rather than its target.
*/
int source_chown(const char *filename, uid_t owner, gid_t group) { int source_chown(const char *filename, uid_t owner, gid_t group) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
return fchownat(handle.root_fd, filename, owner, group, AT_SYMLINK_NOFOLLOW); return fchownat(handle.root_fd, filename, owner, group, AT_SYMLINK_NOFOLLOW);
} }
/**
* @brief Truncate a file to a specified length
*
* Opens the file with read-only access then truncates it. This may fail
* if the file wasn't opened with write permissions. Consider changing
* openat() flags to O_WRONLY for reliability.
*/
int source_truncate(const char *filename, off_t length) { int source_truncate(const char *filename, off_t length) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
int fd = openat(handle.root_fd, relative_filename, 0); int fd = openat(handle.root_fd, relative_filename, 0);
@ -258,22 +155,12 @@ int source_truncate(const char *filename, off_t length) {
return ftruncate(fd, length); return ftruncate(fd, length);
} }
/**
* @brief Open a file with specified flags
*
* Uses openat() to safely access files relative to root_fd.
*/
int source_open(const char *filename, int flags) { int source_open(const char *filename, int flags) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
return openat(handle.root_fd, relative_filename, flags); return openat(handle.root_fd, relative_filename, flags);
} }
/**
* @brief Create and open a new file
*
* Uses openat() with O_CREAT to create files relative to root_fd.
*/
int source_create(const char *filename, int flags, mode_t mode) { int source_create(const char *filename, int flags, mode_t mode) {
const char *relative_filename = source_filename_translate(filename); const char *relative_filename = source_filename_translate(filename);
return openat(handle.root_fd, relative_filename, flags | O_CREAT, mode); return openat(handle.root_fd, relative_filename, flags, mode);
} }

View File

@ -13,129 +13,48 @@
#include <sys/stat.h> #include <sys/stat.h>
/** /**
* @brief Initialize the source filesystem with a root directory * Initializes the source file handling.
* *
* Sets up the mount point and opens a protected file descriptor to the * @param root_path The root of the source files folder.
* root directory for safe relative operations. * @return 0 on success, -1 on failure.
*
* @param root_path Absolute path to the physical root directory
* @return 0 on success, -1 on failure
*/ */
int source_init(const char *root_path); int source_init(const char *root_path);
/**
* @brief Clean up resources used by the source filesystem
*/
void source_destroy(void); void source_destroy(void);
/** /* All of the functions below are designed to behave exactly as their non-source
* @brief Get file status information * counterparts. */
*
* Uses fstatat() to retrieve metadata relative to root_fd. Follows symlinks
* by default (flags=0).
*/
int source_stat(const char *restrict filename, struct stat *restrict statbuf); int source_stat(const char *restrict filename, struct stat *restrict statbuf);
/** struct dirent *source_readdir(DIR *dirp);
* @brief Open a directory for reading
*
* Combines openat() and fdopendir() to safely access directories relative
* to the root_fd.
*/
DIR *source_opendir(const char *filename); DIR *source_opendir(const char *filename);
/**
* @brief Remove a file from the filesystem
*
* Uses unlinkat() with AT_REMOVEDIR flag to safely remove files relative
* to the root directory.
*/
int source_unlink(const char *filename); int source_unlink(const char *filename);
/**
* @brief Create a directory in the filesystem
*
* Uses mkdirat() to safely create directories relative to root_fd,
* preventing race conditions from concurrent path modifications.
*/
int source_mkdir(const char *filename, mode_t mode); int source_mkdir(const char *filename, mode_t mode);
/**
* @brief Remove an empty directory
*
* Uses unlinkat() with AT_REMOVEDIR flag to safely remove directories.
*/
int source_rmdir(const char *filename); int source_rmdir(const char *filename);
/**
* @brief Create a symbolic link
*
* Creates symlinks relative to the root_fd directory for safety.
*/
int source_symlink(const char *target, const char *linkpath); int source_symlink(const char *target, const char *linkpath);
/**
* @brief Rename a file or directory
*
* Uses renameat() to safely rename within the same root_fd namespace.
*/
int source_rename(const char *oldpath, const char *newpath); int source_rename(const char *oldpath, const char *newpath);
/**
* @brief Create a hard link
*
* Uses linkat() with flags=0 (default behavior). May need AT_SYMLINK_NOFOLLOW
* if symlink handling should be modified.
*/
int source_link(const char *oldpath, const char *newpath); int source_link(const char *oldpath, const char *newpath);
/**
* @brief Change file access mode
*
* Uses fchmodat() with flags=0 (follow symlinks). Consider using
* AT_SYMLINK_NOFOLLOW if symlink metadata should be modified directly.
*/
int source_chmod(const char *filename, mode_t mode); int source_chmod(const char *filename, mode_t mode);
/**
* @brief Change file owner and group
*
* Uses fchownat() with AT_SYMLINK_NOFOLLOW to modify symlink metadata
* rather than its target.
*/
int source_chown(const char *filename, uid_t owner, gid_t group); int source_chown(const char *filename, uid_t owner, gid_t group);
/**
* @brief Truncate a file to a specified length
*
* Opens the file with read-only access then truncates it. This may fail
* if the file wasn't opened with write permissions. Consider changing
* openat() flags to O_WRONLY for reliability.
*/
int source_truncate(const char *filename, off_t length); int source_truncate(const char *filename, off_t length);
/**
* @brief Check file access permissions
*
* Uses faccessat() to check access rights relative to root_fd.
*/
int source_access(const char *filename, int mode); int source_access(const char *filename, int mode);
/* `open` and `create` are designed to correspond to fuse operations, not the /* `open` and `create` are designed to correspond to fuse operations, not the
* libc's `open(2)`. Both of them actually call `openat`. */ * libc's `open(2)`. Both of them actually call `openat`. */
/**
* @brief Open a file with specified flags
*
* Uses openat() to safely access files relative to root_fd.
*/
int source_open(const char *filename, int flags); int source_open(const char *filename, int flags);
/**
* @brief Create and open a new file
*
* Uses openat() with O_CREAT to create files relative to root_fd.
*/
int source_create(const char *filename, int flags, mode_t mode); int source_create(const char *filename, int flags, mode_t mode);
#endif // !SOURCEFS_H #endif // !SOURCEFS_H

View File

@ -19,61 +19,26 @@
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#define GC_INTERVAL 1 // Garbage collection time interval.
/**
* @struct temp_process_permissions
* @brief Stores temporary file access permissions for a process
*
* This structure tracks temporary file access permissions for a specific
* process. The permissions are tied to the process's lifetime using its
* creation time.
*/
struct temp_process_permissions { struct temp_process_permissions {
unsigned long long creation_time; /**< Process creation time */ // yes, this is a correct type for start time in jiffies (see
vec(char *) allowed_files; /**< List of allowed file paths (prefixes) */ // proc_pid_stat(5))
vec(char *) denied_files; /**< List of denied file paths (prefixes) */ unsigned long long creation_time;
vec(char *) allowed_files;
vec(char *) denied_files;
}; };
/**
* @brief Global table mapping PIDs to temporary permissions
*
* This map stores the temporary file access permissions for processes.
* Entries are keyed by process ID (pid_t) and contain temp_process_permissions
* structures with the access rules.
*/
map(pid_t, struct temp_process_permissions) temp_permissions_table; map(pid_t, struct temp_process_permissions) temp_permissions_table;
/**
* @brief Read-write lock for thread-safe access to temp_permissions_table
*
* A read-write lock to ensure thread-safe operations on the
* temp_permissions_table. Allows concurrent reads but exclusive writes.
*/
pthread_rwlock_t temp_permissions_table_lock = PTHREAD_RWLOCK_INITIALIZER; pthread_rwlock_t temp_permissions_table_lock = PTHREAD_RWLOCK_INITIALIZER;
/**
* @brief Thread handle for the garbage collector
*
* This thread handles cleanup of stale entries in the temp_permissions_table.
*/
pthread_t gc_thread; pthread_t gc_thread;
/**
* @brief Flag indicating whether garbage collector is active
*
* When non-zero, indicates that the garbage collector thread should continue
* running.
*/
int is_gc_active = 0; int is_gc_active = 0;
/** /**
* Function to get the process creation time from the proc * Function to get the process creation time (in jiffies) from the proc
* filesystem * filesystem
* *
* @param pid The process ID of the process to get the creation time of * @param pid: The process ID of the process to get the creation time of
* @return The process creation time, or 0 on error * @return: The process creation time in jiffies, or 0 on error
* @note although nothing in the documentation says that the creation time is * @note: although nothing in the documentation says that the creation time is
* never really equal to 0, it exceptionally unlikely. * never really equal to 0, it exceptionally unlikely.
*/ */
unsigned long long get_process_creation_time(pid_t pid) { unsigned long long get_process_creation_time(pid_t pid) {
@ -112,23 +77,12 @@ unsigned long long get_process_creation_time(pid_t pid) {
return creation_time; return creation_time;
} }
/**
* @brief Validates if a process entry is still valid
*
* Checks if the given process entry matches the current process state
* by comparing creation times.
*
* @param pid Process ID to validate
* @param entry Pointer to the permission entry to validate
* @return 1 if valid, 0 if invalid or error
*/
int is_valid(pid_t pid, struct temp_process_permissions *entry) { int is_valid(pid_t pid, struct temp_process_permissions *entry) {
unsigned long long creation_time = get_process_creation_time(pid); unsigned long long creation_time = get_process_creation_time(pid);
if (creation_time == 0) { if (creation_time == 0) {
return 0; return 0;
} }
// If the creation time doesn't match, this is a different process instance
if (creation_time != entry->creation_time) { if (creation_time != entry->creation_time) {
return 0; return 0;
} }
@ -136,30 +90,19 @@ int is_valid(pid_t pid, struct temp_process_permissions *entry) {
return 1; return 1;
} }
/**
* @brief Garbage collector thread for cleaning up stale entries
*
* Periodically scans the permissions table and removes entries for processes
* that no longer exist. Runs every second while is_gc_active is true.
*
* @param arg Unused thread argument parameter
* @return Always NULL
*/
void *garbage_collector(void *arg) { void *garbage_collector(void *arg) {
(void)arg; (void)arg;
while (is_gc_active) { while (is_gc_active) {
sleep(GC_INTERVAL); // Check once per second for stale entries sleep(1);
pthread_rwlock_wrlock(&temp_permissions_table_lock); pthread_rwlock_wrlock(&temp_permissions_table_lock);
vec(pid_t) blacklist; vec(pid_t) blacklist;
init(&blacklist); init(&blacklist);
// Identify stale entries
for_each(&temp_permissions_table, pid, entry) { for_each(&temp_permissions_table, pid, entry) {
if (!is_valid(*pid, entry)) { if (!is_valid(*pid, entry)) {
push(&blacklist, *pid); push(&blacklist, *pid);
// Free memory for this entry's file lists
for_each(&entry->allowed_files, allowed_file) { free(*allowed_file); } for_each(&entry->allowed_files, allowed_file) { free(*allowed_file); }
cleanup(&entry->allowed_files); cleanup(&entry->allowed_files);
for_each(&entry->denied_files, denied_file) { free(*denied_file); } for_each(&entry->denied_files, denied_file) { free(*denied_file); }
@ -167,7 +110,6 @@ void *garbage_collector(void *arg) {
} }
} }
// Remove all stale entries from the table
for_each(&blacklist, pid) { erase(&temp_permissions_table, *pid); } for_each(&blacklist, pid) { erase(&temp_permissions_table, *pid); }
cleanup(&blacklist); cleanup(&blacklist);
@ -181,7 +123,7 @@ void *garbage_collector(void *arg) {
/** /**
* Initializes the temporary permissions table. * Initializes the temporary permissions table.
* *
* @return 0 on success, -1 on failure * @return: 0 on success, -1 on failure (e.g. ENOMEM)
*/ */
int init_temp_permissions_table(void) { int init_temp_permissions_table(void) {
init(&temp_permissions_table); init(&temp_permissions_table);
@ -190,7 +132,7 @@ int init_temp_permissions_table(void) {
/** /**
* Starts the temporary permissions table garbage_collector. * Starts the temporary permissions table garbage_collector.
* *
* @return 0 on success, -1 on failure * @return: 0 on success, -1 on failure (e.g. ENOMEM)
*/ */
int init_garbage_collector(void) { int init_garbage_collector(void) {
is_gc_active = 1; is_gc_active = 1;
@ -203,7 +145,7 @@ int init_garbage_collector(void) {
/** /**
* Destroys the temporary permissions table. * Destroys the temporary permissions table.
* *
* @note the table is guranteed to be destroyed if it is already initialized. * @note: the table is guranteed to be destroyed if it is already initialized.
* It does not indicate any errors whatsoever. If something goes wrong - you are * It does not indicate any errors whatsoever. If something goes wrong - you are
* screwed. * screwed.
*/ */
@ -213,7 +155,7 @@ void destroy_temp_permissions_table(void) {
pthread_join(gc_thread, NULL); pthread_join(gc_thread, NULL);
} }
// Free the memory allocated for the table // free the memory allocated for the table
for_each(&temp_permissions_table, entry) { for_each(&temp_permissions_table, entry) {
for_each(&entry->allowed_files, allowed_file) { free(*allowed_file); } for_each(&entry->allowed_files, allowed_file) { free(*allowed_file); }
cleanup(&entry->allowed_files); cleanup(&entry->allowed_files);
@ -227,17 +169,15 @@ void destroy_temp_permissions_table(void) {
} }
/** /**
* @brief Checks if a specific process has temporary access to a file * Checks if the process has a temporary access to the file.
* *
* Checks only the specified process (not its parents) for temporary access * @param filename: The file that the process is trying to access
* permissions to the given file. Uses a longest-match algorithm for path * @param pid: PID of the process
* prefixes. * @return: access status - ALLOW, DENY or NDEF in case if no information was
* * found is avaliable
* @param filename Path to the file being accessed
* @param pid Process ID of the process to check
* @return Access status ALLOW, DENY, or NDEF (no information)
*/ */
access_t check_temp_access_noparent(const char *filename, pid_t pid) { access_t check_temp_access_noparent(const char *filename, pid_t pid) {
// TODO: more efficient locking
pthread_rwlock_rdlock(&temp_permissions_table_lock); pthread_rwlock_rdlock(&temp_permissions_table_lock);
struct temp_process_permissions *permission_entry = struct temp_process_permissions *permission_entry =
get(&temp_permissions_table, pid); get(&temp_permissions_table, pid);
@ -250,15 +190,13 @@ access_t check_temp_access_noparent(const char *filename, pid_t pid) {
} }
if (process_creation_time == permission_entry->creation_time) { if (process_creation_time == permission_entry->creation_time) {
// The process is the same as the one that was granted temporary access // the process is the same as the one that was granted temporary access
// to the file
size_t filename_len = strlen(filename); size_t filename_len = strlen(filename);
access_t ret = NDEF; access_t ret = NDEF;
size_t maxlen = 0; size_t maxlen = 0;
// Check denied files first (deny takes precedence over allow)
for_each(&permission_entry->denied_files, denied_file) { for_each(&permission_entry->denied_files, denied_file) {
size_t denied_file_len = strlen(*denied_file); size_t denied_file_len = strlen(*denied_file);
// Check if this denied path prefix matches the requested file
if ((strncmp(*denied_file, filename, denied_file_len) == 0 && if ((strncmp(*denied_file, filename, denied_file_len) == 0 &&
((denied_file_len < filename_len && ((denied_file_len < filename_len &&
(*denied_file)[denied_file_len - 1] == '/') || (*denied_file)[denied_file_len - 1] == '/') ||
@ -268,21 +206,17 @@ access_t check_temp_access_noparent(const char *filename, pid_t pid) {
ret = DENY; ret = DENY;
} }
} }
// Check allowed files
for_each(&permission_entry->allowed_files, allowed_file) { for_each(&permission_entry->allowed_files, allowed_file) {
size_t allowed_file_len = strlen(*allowed_file); size_t allowed_file_len = strlen(*allowed_file);
// Check if this allowed path prefix matches the requested file
if ((strncmp(*allowed_file, filename, allowed_file_len) == 0 && if ((strncmp(*allowed_file, filename, allowed_file_len) == 0 &&
((allowed_file_len < filename_len && ((allowed_file_len < filename_len &&
(*allowed_file)[allowed_file_len - 1] == '/') || (*allowed_file)[allowed_file_len - 1] == '/') ||
(allowed_file_len == filename_len))) && (allowed_file_len == filename_len))) &&
allowed_file_len > maxlen) { allowed_file > maxlen) {
maxlen = allowed_file_len; maxlen = allowed_file_len;
ret = ALLOW; ret = ALLOW;
} }
} }
pthread_rwlock_unlock(&temp_permissions_table_lock); pthread_rwlock_unlock(&temp_permissions_table_lock);
return ret; return ret;
} }
@ -295,12 +229,12 @@ access_t check_temp_access_noparent(const char *filename, pid_t pid) {
* Checks if the process or any of it's parents have temporary access to the * Checks if the process or any of it's parents have temporary access to the
* file. * file.
* *
* @param filename The file that the process is trying to access * @param filename: The file that the process is trying to access
* @param pi The process information * @param pi: The process information
* @return access status - ALLOW, DENY or NDEF in case if no information was * @return: access status - ALLOW, DENY or NDEF in case if no information was
* found. Does not return ALLOW_TEMP. * found. Does not return ALLOW_TEMP.
* @note In case one of the parent processes is killed while this function * @note: In case one of the parent processes is killed while this function
* execution the result is not guranteed to be correct. It should only * execution the result is not guranteed to be correct. It should only lead to
* false negatives, though. * false negatives, though.
*/ */
access_t check_temp_access(const char *filename, struct process_info pi) { access_t check_temp_access(const char *filename, struct process_info pi) {
@ -319,11 +253,11 @@ access_t check_temp_access(const char *filename, struct process_info pi) {
/** /**
* Sets temporary access mode of the process to the file. * Sets temporary access mode of the process to the file.
* *
* @param filename The file that the process is trying to access * @param filename: The file that the process is trying to access
* @param pi The process information * @param pi: The process information
* @param mode Kind of access rule to be set - SET_DENY to deny access, and * @param mode: Kind of access rule to be set - SET_DENY to deny access, and
* SET_ALLOW to allow access. * SET_ALLOW to allow access.
* @return 0 on success, -1 on failure. * @return: 0 on success, -1 on failure.
*/ */
int set_temp_access(const char *filename, struct process_info pi, int set_temp_access(const char *filename, struct process_info pi,
set_mode_t mode) { set_mode_t mode) {
@ -344,7 +278,8 @@ int set_temp_access(const char *filename, struct process_info pi,
} }
if (process_creation_time == permission_entry->creation_time) { if (process_creation_time == permission_entry->creation_time) {
// The process is the same as the one that was granted temporary access // the process is the same as the one that was granted temporary access
// to the file
if (mode == SET_ALLOW) { if (mode == SET_ALLOW) {
push(&permission_entry->allowed_files, strdup(filename)); push(&permission_entry->allowed_files, strdup(filename));
} }
@ -355,19 +290,19 @@ int set_temp_access(const char *filename, struct process_info pi,
pthread_rwlock_unlock(&temp_permissions_table_lock); pthread_rwlock_unlock(&temp_permissions_table_lock);
return 0; return 0;
} }
// We have an entry for the process, but the process is different // we have an entry for the process, but the process is different
// Delete the entry and create a new one // delete the entry and create a new one
erase(&temp_permissions_table, pi.PID); erase(&temp_permissions_table, pi.PID);
permission_entry = NULL; permission_entry = NULL;
} }
// No entry is present - construct a new one // no entry is present
// construct the entry
struct temp_process_permissions new_permission_entry; struct temp_process_permissions new_permission_entry;
new_permission_entry.creation_time = get_process_creation_time(pi.PID); new_permission_entry.creation_time = get_process_creation_time(pi.PID);
init(&new_permission_entry.allowed_files); init(&new_permission_entry.allowed_files);
init(&new_permission_entry.denied_files); init(&new_permission_entry.denied_files);
if (mode == SET_ALLOW) { if (mode == SET_ALLOW) {
push(&new_permission_entry.allowed_files, strdup(filename)); push(&new_permission_entry.allowed_files, strdup(filename));
} }

View File

@ -16,21 +16,21 @@
/** /**
* Initializes the temporary permissions table. * Initializes the temporary permissions table.
* *
* @return: 0 on success, -1 on failure * @return: 0 on success, -1 on failure (e.g. ENOMEM)
*/ */
int init_temp_permissions_table(void); int init_temp_permissions_table(void);
/** /**
* Starts the temporary permissions table garbage_collector. * Starts the temporary permissions table garbage_collector.
* *
* @return 0 on success, -1 on failure * @return: 0 on success, -1 on failure (e.g. ENOMEM)
*/ */
int init_garbage_collector(void); int init_garbage_collector(void);
/** /**
* Destroys the temporary permissions table. * Destroys the temporary permissions table.
* *
* @note the table is guranteed to be destroyed if it is already initialized. * @note: the table is guranteed to be destroyed if it is already initialized.
* It does not indicate any errors whatsoever. If something goes wrong - you are * It does not indicate any errors whatsoever. If something goes wrong - you are
* screwed. * screwed.
*/ */
@ -40,12 +40,12 @@ void destroy_temp_permissions_table(void);
* Checks if the process or any of it's parents have temporary access to the * Checks if the process or any of it's parents have temporary access to the
* file. * file.
* *
* @param filename The file that the process is trying to access * @param filename: The file that the process is trying to access
* @param pi The process information * @param pi: The process information
* @return access status - ALLOW, DENY or NDEF in case if no information was * @return: access status - ALLOW, DENY or NDEF in case if no information was
* found. Does not return ALLOW_TEMP. * found. Does not return ALLOW_TEMP.
* @note In case one of the parent processes is killed while this function * @note: In case one of the parent processes is killed while this function
* execution the result is not guranteed to be correct. It should only * execution the result is not guranteed to be correct. It should only lead to
* false negatives, though. * false negatives, though.
*/ */
access_t check_temp_access(const char *filename, struct process_info pi); access_t check_temp_access(const char *filename, struct process_info pi);
@ -53,11 +53,11 @@ access_t check_temp_access(const char *filename, struct process_info pi);
/** /**
* Sets temporary access mode of the process to the file. * Sets temporary access mode of the process to the file.
* *
* @param filename The file that the process is trying to access * @param filename: The file that the process is trying to access
* @param pi The process information * @param pi: The process information
* @param mode Kind of access rule to be set - SET_DENY to deny access, and * @param mode: Kind of access rule to be set - SET_DENY to deny access, and
* SET_ALLOW to allow access. * SET_ALLOW to allow access.
* @return 0 on success, -1 on failure. * @return: 0 on success, -1 on failure.
*/ */
int set_temp_access(const char *filename, struct process_info pi, int set_temp_access(const char *filename, struct process_info pi,
set_mode_t mode); set_mode_t mode);

View File

@ -10,7 +10,7 @@
#include <stddef.h> #include <stddef.h>
#include <sys/types.h> #include <sys/types.h>
#include <time.h> #include <time.h>
#define _GNU_SOURCE // Required for certain POSIX extensions #define _GNU_SOURCE
#include "cc.h" #include "cc.h"
#include "perm_permissions_table.h" #include "perm_permissions_table.h"
#include "real_filename.h" #include "real_filename.h"
@ -26,45 +26,34 @@
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
// Exit status codes for icfs_dialogue process interaction
#define DIALOGUE_YES 1 #define DIALOGUE_YES 1
#define DIALOGUE_NO 0 #define DIALOGUE_NO 0
#define DIALOGUE_PERM 2 #define DIALOGUE_PERM 2
#define DIALOGUE_TEMP 0 // Bitmask position, not value #define DIALOGUE_TEMP 0
// Mutex to protect concurrent access to permission tables
pthread_mutex_t access_check_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t access_check_mutex = PTHREAD_MUTEX_INITIALIZER;
// Structure to hold user permission decision response
struct dialogue_response { struct dialogue_response {
access_t decision; access_t decision;
char *filename; char *filename;
}; };
/**
* Initialize UI socket and required permission tables
*
* @param perm_permissions_db_filename Path to persistent permissions DB
* @return 0 on success, 1 on failure
*/
int init_ui_socket(const char *perm_permissions_db_filename) { int init_ui_socket(const char *perm_permissions_db_filename) {
FILE *fp = NULL; FILE *fp = NULL;
// Initialize in-memory temporary permissions table
if (init_temp_permissions_table()) { if (init_temp_permissions_table()) {
fprintf(stderr, fprintf(stderr,
"[ICFS] Could not initialize temporary permissions table.\n"); "[ICFS] Could not initialize temporary permissions table.\n");
return 1; return 1;
} }
// Initialize permanent permissions database
if (init_perm_permissions_table(perm_permissions_db_filename)) { if (init_perm_permissions_table(perm_permissions_db_filename)) {
fprintf(stderr, fprintf(stderr,
"[ICFS] Could not initialize permanent permissions table.\n"); "[ICFS] Could not initialize permanent permissions table.\n");
return 1; return 1;
} }
// Verify dialogue utility is available // Test if dialogue is installed (get version)
fp = popen("icfs_dialogue --version", "r"); fp = popen("icfs_dialogue --version", "r");
if (fp == NULL) { if (fp == NULL) {
perror("[ICFS] Pipe returned an error"); perror("[ICFS] Pipe returned an error");
@ -75,23 +64,19 @@ int init_ui_socket(const char *perm_permissions_db_filename) {
return 0; return 0;
} }
/**
* Clean up UI socket resources
*/
void destroy_ui_socket(void) { void destroy_ui_socket(void) {
destroy_temp_permissions_table(); destroy_temp_permissions_table();
destroy_perm_permissions_table(); destroy_perm_permissions_table();
} }
/** /**
* Query user for file access permission through GUI dialogue * Asks the user if the process should be allowed to access the file using the
* GUI
* *
* Constructs and executes the icfs_dialogue command to get user consent. * @param filename: The file that the process is trying to access
* Handles memory allocation errors gracefully and parses the response. * @param pi: The process information
* * @return: access status - ALLOW, DENY or ALLOW_TEMP
* @param filename File being accessed * allowed for the runtime of the process
* @param proc_info Process requesting access
* @return Struct containing access decision and resolved filename
*/ */
struct dialogue_response ask_access(const char *filename, struct dialogue_response ask_access(const char *filename,
struct process_info proc_info) { struct process_info proc_info) {
@ -105,21 +90,26 @@ struct dialogue_response ask_access(const char *filename,
response.filename = NULL; response.filename = NULL;
if (ret < 0) { if (ret < 0) {
// Memory allocation failed - create minimal fallback response // If asprintf fails, the contents of command are undefined (see man
// asprintf). That does not explicitly rule out that command will be a valid
// pointer. But the risk of freeing a non-allocated pointer is too much to
// justify preparing for this.
fprintf(stderr, "[ICFS] Could not create query on rule insertion"); fprintf(stderr, "[ICFS] Could not create query on rule insertion");
perror(""); perror("");
response.decision = DENY;
response.filename = malloc(2); response.filename = malloc(2);
response.filename[0] = '/'; response.filename[0] = '/';
response.filename[1] = 0; response.filename[1] = 0;
return response; return response;
} }
// Execute permission dialogue // dialogue Question Message Popup
fp = popen(command, "r"); fp = popen(command, "r");
free(command); free(command);
if (fp == NULL) { if (fp == NULL) {
perror("[ICFS] Pipe returned a error"); perror("[ICFS] Pipe returned a error");
response.decision = DENY;
response.filename = malloc(2); response.filename = malloc(2);
response.filename[0] = '/'; response.filename[0] = '/';
response.filename[1] = 0; response.filename[1] = 0;
@ -129,9 +119,9 @@ struct dialogue_response ask_access(const char *filename,
str(char) dialogue_output; str(char) dialogue_output;
init(&dialogue_output); init(&dialogue_output);
char line[1024]; char line[1024]; // Buffer to read individual lines
// Read entire command output // Read the command output line by line
while (fgets(line, sizeof(line), fp)) { while (fgets(line, sizeof(line), fp)) {
push_fmt(&dialogue_output, line); push_fmt(&dialogue_output, line);
} }
@ -141,20 +131,20 @@ struct dialogue_response ask_access(const char *filename,
fprintf(stderr, "[ICFS] dialogue wrote out %s\n", first(&dialogue_output)); fprintf(stderr, "[ICFS] dialogue wrote out %s\n", first(&dialogue_output));
fprintf(stderr, "[ICFS] dialogue returned %d\n", dialogue_exit_code); fprintf(stderr, "[ICFS] dialogue returned %d\n", dialogue_exit_code);
// Handle empty output case
if (size(&dialogue_output) == 0) { if (size(&dialogue_output) == 0) {
push(&dialogue_output, '/'); push(&dialogue_output, '/');
} }
// Validate string length consistency
assert(strlen(first(&dialogue_output)) == size(&dialogue_output)); assert(strlen(first(&dialogue_output)) == size(&dialogue_output));
// Allocate and copy final filename
response.filename = malloc(size(&dialogue_output) + 1); response.filename = malloc(size(&dialogue_output) + 1);
strcpy(response.filename, first(&dialogue_output)); strcpy(response.filename, first(&dialogue_output));
cleanup(&dialogue_output); // response.filename[size(&dialogue_output)] = 0;
// assert(0 == strcmp(response.filename, first(&dialogue_output)));
cleanup(&dialogue_output);
time_t now = time(0);
// Parse exit code combination
if (dialogue_exit_code == (DIALOGUE_YES | DIALOGUE_PERM)) { if (dialogue_exit_code == (DIALOGUE_YES | DIALOGUE_PERM)) {
response.decision = ALLOW; response.decision = ALLOW;
} else if (dialogue_exit_code == DIALOGUE_YES | DIALOGUE_TEMP) { } else if (dialogue_exit_code == DIALOGUE_YES | DIALOGUE_TEMP) {
@ -169,58 +159,68 @@ struct dialogue_response ask_access(const char *filename,
} }
/** /**
* Determine file access based on permission tables and user input * Check access according to:
* 1. temp permission table
* 2. permanent permission table
* 3. user descision
* *
* Checks permissions in order: * @param filename: The file that the process is trying to access
* 1. Temporary permission table * @param pi: The process information
* 2. Permanent permission table * @param opts: options (GRANT_TEMP, GRANT_PERM)
* 3. User decision via GUI * @return: 0 if access is denied, 1 if access is allowed
*
* @param filename File being accessed
* @param pi Process information
* @param opts Flags to force permission (GRANT_TEMP/GRANT_PERM)
* @return 1 if access allowed, 0 if denied
*/ */
int interactive_access(const char *filename, struct process_info proc_info, int interactive_access(const char *filename, struct process_info proc_info,
int opts) { int opts) {
char *real_path = real_filename(filename); char *real_path = real_filename(filename);
pthread_mutex_lock(&access_check_mutex); pthread_mutex_lock(&access_check_mutex);
// First check temporary permissions
access_t access = check_temp_access(real_path, proc_info); access_t access = check_temp_access(real_path, proc_info);
if (access == ALLOW) { if (access == ALLOW) {
fprintf(stderr, "[ICFS] Permission allowed to %s based on temp table.\n", fprintf(
stderr,
"[ICFS] Permission allowed to %s based on a rule present in the temp "
"permission table.\n",
proc_info.name); proc_info.name);
free(real_path); free(real_path);
pthread_mutex_unlock(&access_check_mutex); pthread_mutex_unlock(&access_check_mutex);
return 1; return 1;
} }
if (access == DENY) { if (access == DENY) {
fprintf(stderr, "[ICFS] Permission denied to %s based on temp table.\n", fprintf(
stderr,
"[ICFS] Permission denied to %s based on a rule present in the temp "
"permission table.\n",
proc_info.name); proc_info.name);
free(real_path); free(real_path);
pthread_mutex_unlock(&access_check_mutex); pthread_mutex_unlock(&access_check_mutex);
return 0; return 0;
} }
// Then check permanent permissions
access = check_perm_access(real_path, proc_info); access = check_perm_access(real_path, proc_info);
if (access == ALLOW) { if (access == ALLOW) {
fprintf(stderr, "[ICFS] Permission allowed to %s based on perm table.\n", fprintf(
stderr,
"[ICFS] Permission allowed to %s based on a rule present in the perm "
"permission table.\n",
proc_info.name); proc_info.name);
free(real_path); free(real_path);
pthread_mutex_unlock(&access_check_mutex); pthread_mutex_unlock(&access_check_mutex);
return 1; return 1;
} }
if (access == DENY) { if (access == DENY) {
fprintf(stderr, "[ICFS] Permission denied to %s based on perm table.\n", fprintf(
stderr,
"[ICFS] Permission denied to %s based on a rule present in the perm "
"permission table.\n",
proc_info.name); proc_info.name);
free(real_path); free(real_path);
pthread_mutex_unlock(&access_check_mutex); pthread_mutex_unlock(&access_check_mutex);
return 0; return 0;
} }
// Handle forced permission grants // if noth GRANT_TEMP and GRANT_PERM are selected, then only permanent
// permissions are granted
if (opts & GRANT_PERM) { if (opts & GRANT_PERM) {
fprintf(stderr, "[ICFS] Permission granted permanently to %s.\n", fprintf(stderr, "[ICFS] Permission granted permanently to %s.\n",
proc_info.name); proc_info.name);
@ -238,59 +238,66 @@ int interactive_access(const char *filename, struct process_info proc_info,
return 1; return 1;
} }
// Get user decision
struct dialogue_response response = ask_access(filename, proc_info); struct dialogue_response response = ask_access(filename, proc_info);
// fprintf(stderr, "%s", response.filename);
// assert(0 != strlen(response.filename));
// the user might specify a different file in the dialogue, so we need to
// check if it is valid
// Validate returned filename meets requirements:
// 1. Must exist
// 2. Must be prefix of original filename with trailing slash
// or exact match
while ( while (
source_access(response.filename, F_OK) || source_access(response.filename, F_OK) ||
!(strncmp(response.filename, filename, strlen(response.filename)) == 0 && !(strncmp(response.filename, filename, strlen(response.filename)) == 0 &&
((strlen(response.filename) < strlen(filename) && ((strlen(response.filename) < strlen(filename) &&
response.filename[strlen(response.filename) - 1] == '/') || response.filename[strlen(response.filename) - 1] == '/') ||
(strlen(response.filename) == strlen(filename))))) { (strlen(response.filename) == strlen(filename))))) {
fprintf(stderr, "[ICFS] Invalid filename returned by dialogue: %s\n", // if it is invalid, just ask again.
fprintf(stderr,
"[ICFS] Filename returned by access dialogue wasn't correct: %s\n",
response.filename); response.filename);
free(response.filename); free(response.filename);
response = ask_access(filename, proc_info); response = ask_access(filename, proc_info);
} }
free(real_path); free(real_path);
real_path = real_filename(response.filename); real_path = real_filename(response.filename);
free(response.filename); free(response.filename);
int ret = 0; int ret = 0;
// Apply user decision to appropriate permission table
if (response.decision == ALLOW) { if (response.decision == ALLOW) {
fprintf(stderr, fprintf(stderr,
"[ICFS] Permission granted permanently to %s based on response.\n", "[ICFS] Permission granted permanently to %s based on zenty "
"response.\n",
proc_info.name); proc_info.name);
set_perm_access(real_path, proc_info, SET_ALLOW); set_perm_access(real_path, proc_info, SET_ALLOW);
ret = 1; ret = 1;
} else if (response.decision == ALLOW_TEMP) { } else if (response.decision == ALLOW_TEMP) {
fprintf(stderr, fprintf(stderr,
"[ICFS] Permission granted temporarily to %s based on response.\n", "[ICFS] Permission granted temporarily to %s based on zenty "
"response.\n",
proc_info.name); proc_info.name);
set_temp_access(real_path, proc_info, SET_ALLOW); set_temp_access(real_path, proc_info, SET_ALLOW);
ret = 1; ret = 1;
} else if (response.decision == DENY_TEMP) { } else if (response.decision == DENY_TEMP) {
fprintf(stderr, fprintf(
"[ICFS] Permission denied temporarily to %s based on response.\n", stderr,
"[ICFS] Permission denied temporarily to %s based on zenty response.\n",
proc_info.name); proc_info.name);
set_temp_access(real_path, proc_info, SET_DENY); set_temp_access(real_path, proc_info, SET_DENY);
ret = 0; ret = 0;
} else if (response.decision == DENY) { } else if (response.decision == DENY) {
fprintf(stderr, fprintf(
"[ICFS] Permission denied permanently to %s based on response.\n", stderr,
"[ICFS] Permission denied permanently to %s based on zenty response.\n",
proc_info.name); proc_info.name);
set_perm_access(real_path, proc_info, SET_DENY); set_perm_access(real_path, proc_info, SET_DENY);
ret = 0; ret = 0;
} }
pthread_mutex_unlock(&access_check_mutex); pthread_mutex_unlock(&access_check_mutex);
free(real_path); free(real_path);
// deny on unknown options.
return ret; return ret;
} }

View File

@ -6,6 +6,10 @@
See the file LICENSE. See the file LICENSE.
*/ */
/*
* Interface for controlling communication with the UI.
*/
#ifndef UI_SOCKET_H #ifndef UI_SOCKET_H
#define UI_SOCKET_H #define UI_SOCKET_H
@ -13,30 +17,27 @@
#include <sys/types.h> #include <sys/types.h>
/** /**
* Initialize UI socket and required permission tables * Initialize the GUI communication.
* *
* @param perm_permissions_db_filename Path to persistent permissions DB * @return: 0 on success, -1 on faliure.
* @return 0 on success, 1 on failure
*/ */
int init_ui_socket(const char *perm_permissions_db_filename); int init_ui_socket(const char *perm_permissions_db_filename);
/** /**
* Clean up UI socket resources * Close the GUI communication.
*/ */
void destroy_ui_socket(void); void destroy_ui_socket(void);
/** /**
* Determine file access based on permission tables and user input * Check access according to:
* 1. temporary permission table
* 2. permanent permission table
* 3. user descision
* *
* Checks permissions in order: * @param filename: The file that the process is trying to access
* 1. Temporary permission table * @param pi: The process information
* 2. Permanent permission table * @param opts: options (GRANT_TEMP, GRANT_PERM)
* 3. User decision via GUI * @return: 0 if access is denied, 1 if access is allowed
*
* @param filename File being accessed
* @param pi Process information
* @param opts Flags to force permission (GRANT_TEMP/GRANT_PERM)
* @return 1 if access allowed, 0 if denied
*/ */
int interactive_access(const char *filename, struct process_info pi, int opts); int interactive_access(const char *filename, struct process_info pi, int opts);

View File

@ -52,7 +52,8 @@ elif [[ $1 == "--performance" ]]; then
else else
echo "Database protection will not be tested due to the lack of setuid capabilites." echo "Database protection will not be tested due to the lack of setuid capabilites."
echo "To test it, run this script with '--setuid'." echo "To test it, run this script with '--setuid'."
valgrind --leak-check=full -s ../build/icfs -o default_permissions ./protected ./.pt.db & #valgrind --leak-check=full -s ../build/icfs -o default_permissions -o debug ./protected ./.pt.db 2>&1 | grep "==\|zenity\|Permission\|column\|callback\|SQLite" &
valgrind --leak-check=full --show-leak-kinds=all -s ../build/icfs -o default_permissions ./protected ./.pt.db &
sleep 5 sleep 5
fi fi
@ -212,10 +213,14 @@ else
echo "[ICFS-TEST]: permanent permissions database access was not tested due to the lack of seuid bit setting capabilites. To test this, run the script with '--setuid' flag" echo "[ICFS-TEST]: permanent permissions database access was not tested due to the lack of seuid bit setting capabilites. To test this, run the script with '--setuid' flag"
fi fi
# performance testing code
RUNS_NUM=500 RUNS_NUM=500
if [[ $1 == '--performance' ]]; then if [[ $1 == '--performance' ]]; then
#warmup
icfs_dialogue --set-fake-response yes
parallel -j8 ::: "./stress.bash $RUNS_NUM openers/opener10"
icfs_dialogue --set-fake-response yes icfs_dialogue --set-fake-response yes
echo "[ICFS-TEST]: temp permissions" echo "[ICFS-TEST]: temp permissions"
time parallel -j8 ::: "./stress.bash $RUNS_NUM openers/opener7" time parallel -j8 ::: "./stress.bash $RUNS_NUM openers/opener7"
@ -223,6 +228,7 @@ if [[ $1 == '--performance' ]]; then
icfs_dialogue --set-fake-response yes_perm icfs_dialogue --set-fake-response yes_perm
echo "[ICFS-TEST]: perm permissions" echo "[ICFS-TEST]: perm permissions"
time parallel -j8 ::: "./stress.bash $RUNS_NUM openers/opener8" time parallel -j8 ::: "./stress.bash $RUNS_NUM openers/opener8"
fi fi
# unmount # unmount
@ -232,9 +238,11 @@ sleep 0.5
umount "$(realpath ./protected)" umount "$(realpath ./protected)"
sleep 3 sleep 3
# test the same thing, but without ICFS mounted
if [[ $1 == '--performance' ]]; then if [[ $1 == '--performance' ]]; then
#warmup
parallel -j8 ::: "./stress.bash $RUNS_NUM openers/opener9"
echo "[ICFS-TEST]: bare filesystem" echo "[ICFS-TEST]: bare filesystem"
time parallel -j8 ::: "./stress.bash $RUNS_NUM openers/opener9" time parallel -j8 ::: "./stress.bash $RUNS_NUM openers/opener9"
fi fi