Compare commits
	
		
			2 Commits
		
	
	
		
			77775e4097
			...
			22cb958b4f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						22cb958b4f
	
				 | 
					
					
						|||
| 
						
						
							
						
						9b0eb10965
	
				 | 
					
					
						
@@ -48,6 +48,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
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) {
 | 
				
			||||||
@@ -397,8 +403,8 @@ static int xmp_chmod(const char *path, mode_t mode, struct fuse_file_info *fi) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * This filesystem is not designed for multiuser operation (e.g. with
 | 
					 * This filesystem is not designed for multiuser operation (with
 | 
				
			||||||
 * allow_other) so there is little point in having chown implemnted
 | 
					 * allow_other option) 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) {
 | 
				
			||||||
@@ -466,8 +472,6 @@ 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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,13 @@
 | 
				
			|||||||
#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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,45 +23,27 @@
 | 
				
			|||||||
#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";
 | 
				
			||||||
// one row corresponds to a permission to access one file for one executable
 | 
					// Each row represents a permission to access a specific file for an 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;
 | 
				
			||||||
@@ -86,27 +68,13 @@ 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) {
 | 
					/**
 | 
				
			||||||
  (void)colname;
 | 
					 * Creates the permissions table schema in the database.
 | 
				
			||||||
 | 
					 * Called when the table doesn't exist yet.
 | 
				
			||||||
  if (argc < 3) {
 | 
					 *
 | 
				
			||||||
    fprintf(
 | 
					 * @return 0 on success, 1 on failure
 | 
				
			||||||
        stderr,
 | 
					 */
 | 
				
			||||||
        "[ICFS] Unexpected amount of arguments given to the callback: %d.\n",
 | 
					int create_database_schema(void) {
 | 
				
			||||||
        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 "
 | 
				
			||||||
@@ -125,11 +93,12 @@ int create_database_schema() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Ensures that the database schema is correct.
 | 
					 * Ensures the database schema matches expected structure.
 | 
				
			||||||
 | 
					 * Validates table existence and column definitions.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return: 0 if the schema is correct, 1 if the schema could not be corrected.
 | 
					 * @return 0 if schema is valid, 1 if validation failed or repair failed
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int ensure_database_schema() {
 | 
					int ensure_database_schema(void) {
 | 
				
			||||||
  // 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);
 | 
				
			||||||
@@ -145,6 +114,7 @@ int ensure_database_schema() {
 | 
				
			|||||||
    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 =
 | 
				
			||||||
@@ -161,18 +131,20 @@ int ensure_database_schema() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Initializes the permanent permissions table.
 | 
					 * Initializes the permanent permissions database.
 | 
				
			||||||
 | 
					 * Creates/opens the database file and sets up schema.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param db_filename: The filename of the permissions sqlite3 database
 | 
					 * @param db_filename Path to the SQLite database file
 | 
				
			||||||
 * @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) {
 | 
				
			||||||
  // we don't want the group and others to access the db
 | 
					  // Prevent group/others access during file creation
 | 
				
			||||||
  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,
 | 
				
			||||||
@@ -180,12 +152,16 @@ 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);
 | 
					  umask(0); // Restore default umask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // 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,
 | 
				
			||||||
@@ -197,17 +173,18 @@ int init_perm_permissions_table(const char *db_filename) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Destroys the permanent permissions table.
 | 
					 * Closes the database connection and releases resources.
 | 
				
			||||||
 | 
					 * 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 the process has a permanent access to the file.
 | 
					 * Checks if a specific process has permanent access to a file.
 | 
				
			||||||
 | 
					 * Does not check parent processes.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param filename: The file that the process is trying to access
 | 
					 * @param filename Path to the file being accessed
 | 
				
			||||||
 * @param pi: The process information
 | 
					 * @param pi Process information structure
 | 
				
			||||||
 * @return: access status - ALLOW, DENY or NDEF in case if no information was
 | 
					 * @return ALLOW (explicit allow), DENY (explicit deny), or NDEF (no info)
 | 
				
			||||||
 * 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) {
 | 
				
			||||||
@@ -216,10 +193,16 @@ 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);
 | 
				
			||||||
@@ -230,30 +213,24 @@ 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);
 | 
				
			||||||
    if (mode_col) {
 | 
					    ret = mode_col ? ALLOW : DENY;
 | 
				
			||||||
      ret = ALLOW;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      ret = DENY;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  sqlite3_finalize(stmt);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  sqlite3_finalize(stmt);
 | 
				
			||||||
  return ret;
 | 
					  return ret;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Checks if the process or any of it's parents have permanent access to the
 | 
					 * Checks if a process or any of its ancestors have permanent access.
 | 
				
			||||||
 * file.
 | 
					 * Handles hierarchical permission inheritance.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param filename: The file that the process is trying to access
 | 
					 * @param filename Path to the file being accessed
 | 
				
			||||||
 * @param pi: The process information
 | 
					 * @param pi Process information structure
 | 
				
			||||||
 * @return: access status - ALLOW, DENY or NDEF in case if no information was
 | 
					 * @return ALLOW/DENY/NDEF with NDEF meaning no explicit rule found
 | 
				
			||||||
 * found. Does not return ALLOW_TEMP or DENY_TEMP.
 | 
					 * @note May return false negatives if parent processes terminate during check
 | 
				
			||||||
 * @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) {
 | 
				
			||||||
@@ -262,13 +239,17 @@ 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) {
 | 
				
			||||||
@@ -283,11 +264,13 @@ access_t check_perm_access(const char *filename, struct process_info pi) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Gives permanent access to the process to the file.
 | 
					 * Sets a permanent access rule for a process-file combination.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param filename: The file that the process is trying to access
 | 
					 * @param filename Path to the file needing access
 | 
				
			||||||
 * @param pi: The process information
 | 
					 * @param pi Process information
 | 
				
			||||||
 * @return: 0 on success, 1 on failure
 | 
					 * @param mode Kind of access rule to be set - SET_DENY to deny access, and
 | 
				
			||||||
 | 
					 * 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) {
 | 
				
			||||||
@@ -299,19 +282,20 @@ 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;
 | 
					    return 1; // Invalid mode
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,36 +14,38 @@
 | 
				
			|||||||
#include "set_mode_t.h"
 | 
					#include "set_mode_t.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Initializes the permanent permissions table.
 | 
					 * Initializes the permanent permissions database.
 | 
				
			||||||
 | 
					 * Creates/opens the database file and sets up schema.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param db_filename: The filename of the permissions sqlite3 database
 | 
					 * @param db_filename Path to the SQLite database file
 | 
				
			||||||
 * @return: 0 on success, -1 on failure (e.g. ENOMEM)
 | 
					 * @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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Destroys the permanent permissions table.
 | 
					 * Closes the database connection and releases resources.
 | 
				
			||||||
 | 
					 * Should be called when the module is shutting down.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void destroy_perm_permissions_table();
 | 
					void destroy_perm_permissions_table();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Checks if the process has a permanent access to the file.
 | 
					 * Checks if a process or any of its ancestors have permanent access.
 | 
				
			||||||
 | 
					 * Handles hierarchical permission inheritance.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param filename: The file that the process is trying to access
 | 
					 * @param filename Path to the file being accessed
 | 
				
			||||||
 * @param pi: The process information
 | 
					 * @param pi Process information structure
 | 
				
			||||||
 * @return: access status - ALLOW, DENY or NDEF in case if no information was
 | 
					 * @return ALLOW/DENY/NDEF with NDEF meaning no explicit rule found
 | 
				
			||||||
 * found
 | 
					 * @note May return false negatives if parent processes terminate during check
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
access_t check_perm_access(const char *filename, struct process_info pi);
 | 
					access_t check_perm_access(const char *filename, struct process_info pi);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Gives permanent access to the process to the file.
 | 
					 * Sets a permanent access rule for a process-file combination.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param filename: The file that the process is trying to access
 | 
					 * @param filename Path to the file needing access
 | 
				
			||||||
 * @param pi: The process information
 | 
					 * @param pi Process information
 | 
				
			||||||
 * @param mode: Kind of access rule to be set - SET_DENY to deny access, and
 | 
					 * @param mode SET_ALLOW (whitelist) or SET_DENY (blacklist)
 | 
				
			||||||
 * SET_ALLOW to allow access.
 | 
					 * @return 0 on success, 1 on failure
 | 
				
			||||||
 * @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);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,6 @@
 | 
				
			|||||||
  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>
 | 
				
			||||||
@@ -17,13 +16,19 @@
 | 
				
			|||||||
 * @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 -1 on error.
 | 
					 * @return pid_t The process ID (main thread's PID), or 0 on error (invalid tid
 | 
				
			||||||
 | 
					 * 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) {
 | 
				
			||||||
    // errno = EINVAL;
 | 
					    // Invalid TID: TIDs are always positive in Linux
 | 
				
			||||||
    return 0;
 | 
					    return 0;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -45,14 +50,31 @@ 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);
 | 
				
			||||||
@@ -74,6 +96,8 @@ 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);
 | 
				
			||||||
@@ -83,8 +107,8 @@ char *get_process_name_by_pid(const int pid) {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
      name = new_name;
 | 
					      name = new_name;
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // readlink does not set the null character
 | 
					      // readlink does not null-terminate, so we must do it manually
 | 
				
			||||||
      name[len] = 0;
 | 
					      name[len] = '\0';
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -93,16 +117,21 @@ char *get_process_name_by_pid(const int pid) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Finds the parent process ID of a given process.
 | 
					 * @brief Finds the parent process ID of a given process.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param pid: The process ID of the process to find the parent of
 | 
					 * This function parses the `/proc/<pid>/status` file to extract the `PPid`
 | 
				
			||||||
 * @return: The parent process ID, or 0 if the parent process ID could not be
 | 
					 * field, which represents the parent process ID. This is useful for tracing
 | 
				
			||||||
 * found
 | 
					 * process lineage.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @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/%u/status", pid);
 | 
					  snprintf(path, sizeof(path), "/proc/%d/status",
 | 
				
			||||||
 | 
					           pid); // Use %d for signed pid_t
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  FILE *file = fopen(path, "r");
 | 
					  FILE *file = fopen(path, "r");
 | 
				
			||||||
  if (file == NULL) {
 | 
					  if (file == NULL) {
 | 
				
			||||||
@@ -112,6 +141,7 @@ 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;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,14 +11,29 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Finds the parent process ID of a given process.
 | 
					 * @brief Finds the parent process ID of a given process.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param pid: The process ID of the process to find the parent of
 | 
					 * This function parses the `/proc/<pid>/status` file to extract the `PPid`
 | 
				
			||||||
 * @return: The parent process ID, or 0 if the parent process ID could not be
 | 
					 * field, which represents the parent process ID. This is useful for tracing
 | 
				
			||||||
 * found
 | 
					 * process lineage.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -26,8 +41,14 @@ 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 -1 on error.
 | 
					 * @return pid_t The process ID (main thread's PID), or 0 on error (invalid tid
 | 
				
			||||||
 | 
					 * or file read failure).
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
pid_t get_main_thread_pid(pid_t tid);
 | 
					pid_t get_main_thread_pid(pid_t tid);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,18 @@
 | 
				
			|||||||
#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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										145
									
								
								src/sourcefs.c
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								src/sourcefs.c
									
									
									
									
									
								
							@@ -16,20 +16,45 @@
 | 
				
			|||||||
#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;
 | 
					  const char *mountpoint; // Absolute path to the mounted filesystem root
 | 
				
			||||||
  int root_fd;
 | 
					  int root_fd;            // File descriptor for the root directory (O_PATH)
 | 
				
			||||||
} 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");
 | 
				
			||||||
@@ -38,8 +63,9 @@ 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);
 | 
				
			||||||
@@ -48,66 +74,115 @@ 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 memory (+1 for null terminator)
 | 
					  // Allocate space for combined path + null terminator
 | 
				
			||||||
  char *result = malloc(total_len + 1);
 | 
					  char *result = malloc(len1 + len2 + 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);
 | 
				
			||||||
@@ -119,6 +194,11 @@ 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);
 | 
				
			||||||
@@ -126,25 +206,48 @@ 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);
 | 
				
			||||||
@@ -155,12 +258,22 @@ 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, mode);
 | 
					  return openat(handle.root_fd, relative_filename, flags | O_CREAT, mode);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,48 +13,129 @@
 | 
				
			|||||||
#include <sys/stat.h>
 | 
					#include <sys/stat.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Initializes the source file handling.
 | 
					 * @brief Initialize the source filesystem with a root directory
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param root_path The root of the source files folder.
 | 
					 * Sets up the mount point and opens a protected file descriptor to the
 | 
				
			||||||
 * @return 0 on success, -1 on failure.
 | 
					 * 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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @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
 | 
					/**
 | 
				
			||||||
 * counterparts. */
 | 
					 * @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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,26 +19,61 @@
 | 
				
			|||||||
#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 {
 | 
				
			||||||
  // yes, this is a correct type for start time in jiffies (see
 | 
					  unsigned long long creation_time; /**< Process creation time */
 | 
				
			||||||
  // proc_pid_stat(5))
 | 
					  vec(char *) allowed_files; /**< List of allowed file paths (prefixes) */
 | 
				
			||||||
  unsigned long long creation_time;
 | 
					  vec(char *) denied_files;  /**< List of denied file paths (prefixes) */
 | 
				
			||||||
  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 (in jiffies) from the proc
 | 
					 * Function to get the process creation time 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 in jiffies, or 0 on error
 | 
					 * @return The process creation time, 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) {
 | 
				
			||||||
@@ -77,12 +112,23 @@ 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;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -90,19 +136,30 @@ 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(1);
 | 
					    sleep(GC_INTERVAL); // Check once per second for stale entries
 | 
				
			||||||
    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); }
 | 
				
			||||||
@@ -110,6 +167,7 @@ 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);
 | 
				
			||||||
@@ -123,7 +181,7 @@ void *garbage_collector(void *arg) {
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Initializes the temporary permissions table.
 | 
					 * Initializes the temporary permissions table.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return: 0 on success, -1 on failure (e.g. ENOMEM)
 | 
					 * @return 0 on success, -1 on failure
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int init_temp_permissions_table(void) {
 | 
					int init_temp_permissions_table(void) {
 | 
				
			||||||
  init(&temp_permissions_table);
 | 
					  init(&temp_permissions_table);
 | 
				
			||||||
@@ -132,7 +190,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 (e.g. ENOMEM)
 | 
					 * @return 0 on success, -1 on failure
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
int init_garbage_collector(void) {
 | 
					int init_garbage_collector(void) {
 | 
				
			||||||
  is_gc_active = 1;
 | 
					  is_gc_active = 1;
 | 
				
			||||||
@@ -145,7 +203,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.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -155,7 +213,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);
 | 
				
			||||||
@@ -169,15 +227,17 @@ void destroy_temp_permissions_table(void) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Checks if the process has a temporary access to the file.
 | 
					 * @brief Checks if a specific process has temporary access to a file
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param filename: The file that the process is trying to access
 | 
					 * Checks only the specified process (not its parents) for temporary access
 | 
				
			||||||
 * @param pid: PID of the process
 | 
					 * permissions to the given file. Uses a longest-match algorithm for path
 | 
				
			||||||
 * @return: access status - ALLOW, DENY or NDEF in case if no information was
 | 
					 * prefixes.
 | 
				
			||||||
 * 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);
 | 
				
			||||||
@@ -190,13 +250,15 @@ 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] == '/') ||
 | 
				
			||||||
@@ -206,17 +268,21 @@ 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 > maxlen) {
 | 
					            allowed_file_len > 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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -229,12 +295,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 lead to
 | 
					 * execution the result is not guranteed to be correct. It should only
 | 
				
			||||||
 * 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) {
 | 
				
			||||||
@@ -253,11 +319,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) {
 | 
				
			||||||
@@ -278,8 +344,7 @@ 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));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -290,19 +355,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
 | 
					  // No entry is present - construct a new one
 | 
				
			||||||
  // 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));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,21 +16,21 @@
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Initializes the temporary permissions table.
 | 
					 * Initializes the temporary permissions table.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return: 0 on success, -1 on failure (e.g. ENOMEM)
 | 
					 * @return: 0 on success, -1 on failure
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
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 (e.g. ENOMEM)
 | 
					 * @return 0 on success, -1 on failure
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
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 lead to
 | 
					 * execution the result is not guranteed to be correct. It should only
 | 
				
			||||||
 * 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);
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										145
									
								
								src/ui-socket.c
									
									
									
									
									
								
							
							
						
						
									
										145
									
								
								src/ui-socket.c
									
									
									
									
									
								
							@@ -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
 | 
					#define _GNU_SOURCE // Required for certain POSIX extensions
 | 
				
			||||||
#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,34 +26,45 @@
 | 
				
			|||||||
#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
 | 
					#define DIALOGUE_TEMP 0 // Bitmask position, not value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Test if dialogue is installed (get version)
 | 
					  // Verify dialogue utility is available
 | 
				
			||||||
  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");
 | 
				
			||||||
@@ -64,19 +75,23 @@ 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();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Asks the user if the process should be allowed to access the file using the
 | 
					 * Query user for file access permission through GUI dialogue
 | 
				
			||||||
 * GUI
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param filename: The file that the process is trying to access
 | 
					 * Constructs and executes the icfs_dialogue command to get user consent.
 | 
				
			||||||
 * @param pi: The process information
 | 
					 * Handles memory allocation errors gracefully and parses the response.
 | 
				
			||||||
 * @return: access status - ALLOW, DENY or ALLOW_TEMP
 | 
					 *
 | 
				
			||||||
 * allowed for the runtime of the process
 | 
					 * @param filename File being accessed
 | 
				
			||||||
 | 
					 * @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) {
 | 
				
			||||||
@@ -90,26 +105,21 @@ struct dialogue_response ask_access(const char *filename,
 | 
				
			|||||||
  response.filename = NULL;
 | 
					  response.filename = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (ret < 0) {
 | 
					  if (ret < 0) {
 | 
				
			||||||
    // If asprintf fails, the contents of command are undefined (see man
 | 
					    // Memory allocation failed - create minimal fallback response
 | 
				
			||||||
    // 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;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // dialogue Question Message Popup
 | 
					  // Execute permission dialogue
 | 
				
			||||||
  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;
 | 
				
			||||||
@@ -119,9 +129,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]; // Buffer to read individual lines
 | 
					  char line[1024];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Read the command output line by line
 | 
					  // Read entire command output
 | 
				
			||||||
  while (fgets(line, sizeof(line), fp)) {
 | 
					  while (fgets(line, sizeof(line), fp)) {
 | 
				
			||||||
    push_fmt(&dialogue_output, line);
 | 
					    push_fmt(&dialogue_output, line);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -131,20 +141,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));
 | 
				
			||||||
  // response.filename[size(&dialogue_output)] = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // assert(0 == strcmp(response.filename, first(&dialogue_output)));
 | 
					 | 
				
			||||||
  cleanup(&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) {
 | 
				
			||||||
@@ -159,68 +169,58 @@ struct dialogue_response ask_access(const char *filename,
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Check access according to:
 | 
					 * Determine file access based on permission tables and user input
 | 
				
			||||||
 * 1. temp permission table
 | 
					 | 
				
			||||||
 * 2. permanent permission table
 | 
					 | 
				
			||||||
 * 3. user descision
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param filename: The file that the process is trying to access
 | 
					 * Checks permissions in order:
 | 
				
			||||||
 * @param pi: The process information
 | 
					 * 1. Temporary permission table
 | 
				
			||||||
 * @param opts: options (GRANT_TEMP, GRANT_PERM)
 | 
					 * 2. Permanent permission table
 | 
				
			||||||
 * @return: 0 if access is denied, 1 if access is allowed
 | 
					 * 3. User decision via GUI
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @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(
 | 
					    fprintf(stderr, "[ICFS] Permission allowed to %s based on temp table.\n",
 | 
				
			||||||
        stderr,
 | 
					            proc_info.name);
 | 
				
			||||||
        "[ICFS] Permission allowed to %s based on a rule present in the temp "
 | 
					 | 
				
			||||||
        "permission table.\n",
 | 
					 | 
				
			||||||
        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(
 | 
					    fprintf(stderr, "[ICFS] Permission denied to %s based on temp table.\n",
 | 
				
			||||||
        stderr,
 | 
					            proc_info.name);
 | 
				
			||||||
        "[ICFS] Permission denied to %s based on a rule present in the temp "
 | 
					 | 
				
			||||||
        "permission table.\n",
 | 
					 | 
				
			||||||
        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(
 | 
					    fprintf(stderr, "[ICFS] Permission allowed to %s based on perm table.\n",
 | 
				
			||||||
        stderr,
 | 
					            proc_info.name);
 | 
				
			||||||
        "[ICFS] Permission allowed to %s based on a rule present in the perm "
 | 
					 | 
				
			||||||
        "permission table.\n",
 | 
					 | 
				
			||||||
        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(
 | 
					    fprintf(stderr, "[ICFS] Permission denied to %s based on perm table.\n",
 | 
				
			||||||
        stderr,
 | 
					            proc_info.name);
 | 
				
			||||||
        "[ICFS] Permission denied to %s based on a rule present in the perm "
 | 
					 | 
				
			||||||
        "permission table.\n",
 | 
					 | 
				
			||||||
        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;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // if noth GRANT_TEMP and GRANT_PERM are selected, then only permanent
 | 
					  // Handle forced permission grants
 | 
				
			||||||
  // 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,66 +238,59 @@ 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))))) {
 | 
				
			||||||
    // if it is invalid, just ask again.
 | 
					    fprintf(stderr, "[ICFS] Invalid filename returned by dialogue: %s\n",
 | 
				
			||||||
    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 zenty "
 | 
					            "[ICFS] Permission granted permanently to %s based on response.\n",
 | 
				
			||||||
            "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 zenty "
 | 
					            "[ICFS] Permission granted temporarily to %s based on response.\n",
 | 
				
			||||||
            "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(
 | 
					    fprintf(stderr,
 | 
				
			||||||
        stderr,
 | 
					            "[ICFS] Permission denied temporarily to %s based on response.\n",
 | 
				
			||||||
        "[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(
 | 
					    fprintf(stderr,
 | 
				
			||||||
        stderr,
 | 
					            "[ICFS] Permission denied permanently to %s based on response.\n",
 | 
				
			||||||
        "[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;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,10 +6,6 @@
 | 
				
			|||||||
  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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -17,27 +13,30 @@
 | 
				
			|||||||
#include <sys/types.h>
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Initialize the GUI communication.
 | 
					 * Initialize UI socket and required permission tables
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @return: 0 on success, -1 on faliure.
 | 
					 * @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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Close the GUI communication.
 | 
					 * Clean up UI socket resources
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
void destroy_ui_socket(void);
 | 
					void destroy_ui_socket(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Check access according to:
 | 
					 * Determine file access based on permission tables and user input
 | 
				
			||||||
 * 1. temporary permission table
 | 
					 | 
				
			||||||
 * 2. permanent permission table
 | 
					 | 
				
			||||||
 * 3. user descision
 | 
					 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param filename: The file that the process is trying to access
 | 
					 * Checks permissions in order:
 | 
				
			||||||
 * @param pi: The process information
 | 
					 * 1. Temporary permission table
 | 
				
			||||||
 * @param opts: options (GRANT_TEMP, GRANT_PERM)
 | 
					 * 2. Permanent permission table
 | 
				
			||||||
 * @return: 0 if access is denied, 1 if access is allowed
 | 
					 * 3. User decision via GUI
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,8 +52,7 @@ 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 -o debug ./protected ./.pt.db 2>&1 | grep "==\|zenity\|Permission\|column\|callback\|SQLite" &
 | 
					  valgrind --leak-check=full -s ../build/icfs -o default_permissions ./protected ./.pt.db &
 | 
				
			||||||
  valgrind --leak-check=full --show-leak-kinds=all -s ../build/icfs -o default_permissions ./protected ./.pt.db &
 | 
					 | 
				
			||||||
  sleep 5
 | 
					  sleep 5
 | 
				
			||||||
fi
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -213,14 +212,10 @@ 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"
 | 
				
			||||||
@@ -228,7 +223,6 @@ 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
 | 
				
			||||||
@@ -238,11 +232,9 @@ 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
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user