14 Commits

Author SHA1 Message Date
BritishTeapot
b705228685 Made temporary process table work!
Temprorary permissions table now works! It needs a better way for
cleaning though.
2025-03-18 15:45:32 +01:00
BritishTeapot
fe4b8801c0 Moved permission table logic to a separate file 2025-03-18 14:02:42 +01:00
BritishTeapot
e1846440bc Merge branch 'main' into Temp_permission_table 2025-03-18 14:00:57 +01:00
BritishTeapot
47a8845013 Wrote more tests 2025-03-18 13:58:02 +01:00
BritishTeapot
b6ce683364 WIP: Added the initial process table implementation.
Wrote a basic process table implementation with CC's vectors and hash
maps. So far, it looks like it does not really work.
2025-03-18 10:07:45 +01:00
BritishTeapot
70e81d64c1 Added the CC (Convenient Containers) library.
Added the CC library for vectors and hash maps.
2025-03-18 10:05:12 +01:00
BritishTeapot
67a148c7aa Fixed inverted access control permissions bug.
Fixed an (admitedly quite silly) bug that caused the access control
descisions to be inverted.
2025-03-18 10:03:32 +01:00
BritishTeapot
c59123330f Updated .gitignore 2025-03-18 09:55:20 +01:00
730d6bc27d Merge pull request 'Testing' (#4) from Testing into main
Reviewed-on: #4
2025-03-18 09:53:25 +01:00
BritishTeapot
66fd9ed582 Updated .gitignore to exclude test remnants. 2025-03-18 09:49:30 +01:00
BritishTeapot
64abb1ed52 Added test scrpt and a mockup of zenity 2025-03-18 09:48:05 +01:00
BritishTeapot
a65d37f3bc Added testing script
Added the `test/test.bash` script that simulates user input with zenity.
Depends on `xdotool` (couldn't make `ydotool` to work)
2025-03-17 11:59:59 +01:00
3b69fd43e3 Updated README.md with recent build instructions 2025-03-17 11:11:01 +01:00
d292abbba5 Updated Makefile with pkg-config and a test target.
Makefile now gets the necessary `cflags` and `libs` compiler arguments
from `pkg-config` which increases portability and makes adding new
dependencies easier. Also added `TEST` flag, to have automated testing
in the future.
2025-03-17 11:02:07 +01:00
14 changed files with 6934 additions and 52 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
build/* build/*
.clang-tidy .clang-tidy
.cache .cache
test/protected/*
compile_commands.json

View File

@@ -1,13 +1,34 @@
SHELL=/bin/bash SHELL=/bin/bash
# configurable options
SOURCES_DIR := ./src
TESTS_DIR := ./tests
BUILD_DIR := ./build
CC := gcc CC := gcc
CXX := g++ CXX := g++
CFLAGS := -I/usr/include/fuse -D_FILE_OFFSET_BITS=64
LDFLAGS := -lfuse3 -pthread
ifdef DEBUG # dependencies
CFLAGS += -O0 -pedantic -Wall -Wextra -Wcast-align \
PACKAGE_NAMES := fuse3
ifeq ($(TEST), 1)
# PACKAGE_NAMES += check # TODO: use check?
endif
# set up cflags and libs
CFLAGS := -D_FILE_OFFSET_BITS=64
LDFLAGS :=
CFLAGS += $(shell pkg-config --cflags $(PACKAGE_NAMES))
LDFLAGS += $(shell pkg-config --libs $(PACKAGE_NAMES))
ifeq ($(DEBUG),1)
CFLAGS += -O0 -pedantic -g -Wall -Wextra -Wcast-align \
-Wcast-qual -Wdisabled-optimization -Wformat=2 \ -Wcast-qual -Wdisabled-optimization -Wformat=2 \
-Winit-self -Wlogical-op -Wmissing-declarations \ -Winit-self -Wlogical-op -Wmissing-declarations \
-Wmissing-include-dirs -Wredundant-decls -Wshadow \ -Wmissing-include-dirs -Wredundant-decls -Wshadow \
@@ -19,12 +40,32 @@ else
LDFLAGS += LDFLAGS +=
endif endif
SOURCES_DIR := ./src
BUILD_DIR := ./build
build: $(BUILD_DIR)/main.o $(BUILD_DIR)/fuse_operations.o $(BUILD_DIR)/sourcefs.o $(BUILD_DIR)/ui-socket.o # set up targets
TARGETS := icfs
ifeq ($(TEST), 1)
TARGETS += icfs_test
endif
# build!
default: $(TARGETS)
.PHONY: clean
icfs: $(BUILD_DIR)/main.o $(BUILD_DIR)/fuse_operations.o $(BUILD_DIR)/sourcefs.o $(BUILD_DIR)/ui-socket.o $(BUILD_DIR)/temp_permissions_table.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $(BUILD_DIR)/icfs $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $(BUILD_DIR)/icfs
icfs_test: $(BUILD_DIR)/main.o $(BUILD_DIR)/fuse_operations.o $(BUILD_DIR)/sourcefs.o $(BUILD_DIR)/ui-socket.o
$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $(BUILD_DIR)/icfs_test
# $(BUILD_DIR)/icfs_test # TODO: implement testing
$(BUILD_DIR)/test_access_control.o: $(TESTS_DIR)/test_access_control.c
$(CC) $(CFLAGS) -c $< $(LDFLAGS) -o $@
$(BUILD_DIR)/main.o: $(SOURCES_DIR)/main.c $(BUILD_DIR)/main.o: $(SOURCES_DIR)/main.c
$(CC) $(CFLAGS) -c $< $(LDFLAGS) -o $(BUILD_DIR)/main.o $(CC) $(CFLAGS) -c $< $(LDFLAGS) -o $(BUILD_DIR)/main.o
@@ -37,5 +78,8 @@ $(BUILD_DIR)/sourcefs.o: $(SOURCES_DIR)/sourcefs.c $(SOURCES_DIR)/sourcefs.h
$(BUILD_DIR)/ui-socket.o: $(SOURCES_DIR)/ui-socket.c $(SOURCES_DIR)/ui-socket.h $(BUILD_DIR)/ui-socket.o: $(SOURCES_DIR)/ui-socket.c $(SOURCES_DIR)/ui-socket.h
$(CC) $(CFLAGS) -c $< $(LDFLAGS) -o $@ $(CC) $(CFLAGS) -c $< $(LDFLAGS) -o $@
$(BUILD_DIR)/temp_permissions_table.o: $(SOURCES_DIR)/temp_permissions_table.c $(SOURCES_DIR)/temp_permissions_table.h
$(CC) $(CFLAGS) -c $< $(LDFLAGS) -o $@
clean: clean:
rm $(BUILD_DIR)/* rm $(BUILD_DIR)/*.o $(BUILD_DIR)/icfs*

View File

@@ -12,15 +12,17 @@ Traditional access control mechanisms in operating systems allow the same level
## Building ## Building
* Install dependencies - Install dependencies
+ fuse, libfuse (v3 or later) - libfuse3
- Debian: `sudo apt install fuse3 libfuse3-dev` - Debian: `sudo apt install fuse3 libfuse3-dev`
+ zenity - zenity
- Debian: `sudo apt install zenity` - Debian: `sudo apt install zenity`
* Build using `make`: - Build tools
+ In the project directory: `make` - Debian: `sudo apt install gcc make pkg-config`
+ Use `make DEBUG=1` for testing. - Build using `make`:
* Resulting binaries should appear in the `build` directory. - In the project directory: `make`
- Use `make DEBUG=1` for testing.
- Resulting binaries should appear in the `build` directory.
## Usage ## Usage

6418
src/cc.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -264,7 +264,7 @@ static int xmp_unlink(const char *path) {
// fprintf(stderr, "%s, %d\n", path, ask_access(path, pi)); // fprintf(stderr, "%s, %d\n", path, ask_access(path, pi));
if (interactive_access(real_filename(path), pi)) { if (!interactive_access(real_filename(path), pi)) {
free(pi.name); free(pi.name);
return -EACCES; return -EACCES;
} }
@@ -392,7 +392,7 @@ static int xmp_create(const char *path, mode_t mode,
// fprintf(stderr, "%s, %d\n", path, ask_access(path, pi)); // fprintf(stderr, "%s, %d\n", path, ask_access(path, pi));
if (interactive_access(real_filename(path), pi)) { if (!interactive_access(real_filename(path), pi)) {
free(pi.name); free(pi.name);
return -EACCES; return -EACCES;
} }
@@ -417,7 +417,7 @@ static int xmp_open(const char *path, struct fuse_file_info *fi) {
pi.name = get_process_name_by_pid(pi.PID); pi.name = get_process_name_by_pid(pi.PID);
// fprintf(stderr, "%s, %d\n", path, ask_access(path, pi)); // fprintf(stderr, "%s, %d\n", path, ask_access(path, pi));
if (interactive_access(real_filename(path), pi)) { if (!interactive_access(real_filename(path), pi)) {
free(pi.name); free(pi.name);
return -EACCES; return -EACCES;
} }

View File

@@ -36,7 +36,7 @@ int main(int argc, char *argv[]) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
ret = init_ui_socket("/home/fedir/.icfs-sock"); ret = init_ui_socket();
if (ret != 0) { if (ret != 0) {
perror("init_ui_socket"); perror("init_ui_socket");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@@ -45,5 +45,6 @@ int main(int argc, char *argv[]) {
ret = fuse_main(argc, argv, get_fuse_operations(), NULL); ret = fuse_main(argc, argv, get_fuse_operations(), NULL);
free(mountpoint); free(mountpoint);
destroy_ui_socket();
return ret; return ret;
} }

12
src/process_info.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef PROCESS_INFO_H
#define PROCESS_INFO_H
#include <sys/types.h>
struct process_info {
pid_t PID;
const char *name;
uid_t UID;
};
#endif // PROCESS_INFO_H

View File

@@ -0,0 +1,232 @@
/*
ICFS: Interactively Controlled File System
Copyright (C) 2024-2025 Fedir Kovalov
This program can be distributed under the terms of the GNU GPLv2.
See the file LICENSE.
*/
#include "temp_permissions_table.h"
#include "cc.h"
#include "process_info.h"
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
struct temp_process_permissions {
// yes, this is a correct type for start time in jiffies (see
// proc_pid_stat(5))
unsigned long long creation_time;
vec(char *) allowed_files;
};
map(pid_t, struct temp_process_permissions) temp_permissions_table;
pthread_mutex_t temp_permissions_table_lock;
/**
* Function to get the process creation time (in jiffies) from the proc
* filesystem
*
* @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
* @note: although nothing in the documentation says that the creation time is
* never really equal to 0, it exceptionally unlikely.
*/
unsigned long long get_process_creation_time(pid_t pid) {
char path[32];
FILE *fp;
unsigned long long creation_time = 0;
// Construct the path to the process's status file
snprintf(path, sizeof(path), "/proc/%u/stat", pid);
// Open the status file
fp = fopen(path, "r");
if (fp == NULL) {
perror("fopen");
return 0;
}
// Read the creation time (the 22nd field in the stat file)
for (int i = 1; i < 22; i++) {
if (fscanf(fp, "%*s") == EOF) {
fprintf(stderr, "Error reading process stat file on the number %d\n", i);
fclose(fp);
return 0;
}
}
if (fscanf(fp, "%llu", &creation_time) != 1) {
fprintf(stderr, "Error reading creation time\n");
fclose(fp);
return 0;
}
// Close the file
fclose(fp);
return creation_time;
}
/**
* Initializes the temporary permissions table.
*
* @return: 0 on success, -1 on failure (e.g. ENOMEM)
*/
int init_temp_permissions_table() {
pthread_mutex_init(&temp_permissions_table_lock, PTHREAD_MUTEX_DEFAULT);
init(&temp_permissions_table);
}
/**
* Destroys the temporary permissions table.
*
* @note: the table is guranteed to be destroyed if it is already initialized
*/
void destroy_temp_permissions_table() {
// free the memory allocated for the table
for_each(&temp_permissions_table, entry) {
for_each(&entry->allowed_files, allowed_file) { free(*allowed_file); }
cleanup(&entry->allowed_files);
}
cleanup(&temp_permissions_table);
pthread_mutex_destroy(&temp_permissions_table_lock);
}
/**
* Checks if the process has a temporary access to the file.
*
* @param filename: The file that the process is trying to access
* @pram pid: PID of the process
* @return: 0 if access is denied, 1 if access is allowed
*/
int check_temp_access_noparent(const char *filename, pid_t pid) {
// TODO: more efficient locking
pthread_mutex_lock(&temp_permissions_table_lock);
struct temp_process_permissions *permission_entry =
get(&temp_permissions_table, pid);
if (permission_entry != NULL) {
unsigned long long process_creation_time = get_process_creation_time(pid);
if (process_creation_time == 0) {
perror("Could not retrieve process creation time");
pthread_mutex_unlock(&temp_permissions_table_lock);
return 0;
}
if (process_creation_time == permission_entry->creation_time) {
// the process is the same as the one that was granted temporary access
// to the file
for_each(&permission_entry->allowed_files, allowed_file) {
if (strncmp(*allowed_file, filename, strlen(filename)) == 0) {
pthread_mutex_unlock(&temp_permissions_table_lock);
return 1;
}
}
}
}
pthread_mutex_unlock(&temp_permissions_table_lock);
return 0;
}
/**
* Finds the parent process ID of a given process.
*
* @param pid: The process ID of the process to find the parent of
* @return: The parent process ID, or 0 if the parent process ID could not be
* found
*/
pid_t get_parent_pid(pid_t pid) {
pid_t ppid = 0;
char path[256];
snprintf(path, sizeof(path), "/proc/%u/status", pid);
FILE *file = fopen(path, "r");
if (file == NULL) {
perror("Failed to open /proc/<pid>/status");
return 0;
}
char line[256];
while (fgets(line, sizeof(line), file)) {
if (sscanf(line, "PPid:\t%d", &ppid) == 1) {
fclose(file);
return ppid;
}
}
fclose(file);
return 0; // Parent PID not found
}
/**
* Checks if the process or any of it's parents have temporary access to the
* file.
*
* @param filename: The file that the process is trying to access
* @pram pi: The process information
* @return: 0 if access is denied, 1 if access is allowed
* @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.
*/
int check_temp_access(const char *filename, struct process_info pi) {
pid_t current_pid = pi.PID;
while (current_pid != 0) {
if (check_temp_access_noparent(filename, current_pid)) {
return 1;
}
current_pid = get_parent_pid(current_pid);
}
return 0;
}
/**
* Gives temporary access to the process to the file.
*
* @param filename: The file that the process is trying to access
* @param pi: The process information
* @return: 0 on success, -1 on failure (e.g. ENOMEM)
*/
int give_temp_access(const char *filename, struct process_info pi) {
pthread_mutex_lock(&temp_permissions_table_lock);
struct temp_process_permissions *permission_entry =
get(&temp_permissions_table, pi.PID);
if (permission_entry != NULL) {
unsigned long long process_creation_time =
get_process_creation_time(pi.PID);
if (process_creation_time == 0) {
perror("Could not retrieve process creation time");
pthread_mutex_unlock(&temp_permissions_table_lock);
return -1;
}
if (process_creation_time == permission_entry->creation_time) {
// the process is the same as the one that was granted temporary access
// to the file
push(&permission_entry->allowed_files, strdup(filename));
pthread_mutex_unlock(&temp_permissions_table_lock);
return 0;
}
// we have an entry for the process, but the process is different
// delete the entry and create a new one
erase(&temp_permissions_table, pi.PID);
permission_entry = NULL;
}
// no entry is present
// construct the entry
struct temp_process_permissions new_permission_entry;
new_permission_entry.creation_time = get_process_creation_time(pi.PID);
init(&new_permission_entry.allowed_files);
push(&new_permission_entry.allowed_files, strdup(filename));
insert(&temp_permissions_table, pi.PID, new_permission_entry);
printf("temp_permissions_table size: %ld\n", size(&temp_permissions_table));
pthread_mutex_unlock(&temp_permissions_table_lock);
return 0;
}

View File

@@ -0,0 +1,39 @@
#ifndef TEMP_PERMISSIONS_TABLE_H
#define TEMP_PERMISSIONS_TABLE_H
#include "process_info.h"
/**
* Initializes the temporary permissions table.
*
* @return: 0 on success, -1 on failure (e.g. ENOMEM)
*/
int init_temp_permissions_table();
/**
* Destroys the temporary permissions table.
*
* @note: the table is guranteed to be destroyed if it is already initialized
*/
void destroy_temp_permissions_table();
/**
* Checks if the process has a temporary access to the file.
*
* @param filename: The file that the process is trying to access
* @param pi: The process information
* @return: 0 if access is denied, 1 if access is allowed
*/
int check_temp_access(const char *filename, struct process_info pi);
/**
* Gives temporary access to the process to the file.
*
* @param filename: The file that the process is trying to access
* @param pi: The process information
* @return: 0 on success, -1 on failure (e.g. ENOMEM)
*/
int give_temp_access(const char *filename, struct process_info pi);
#endif // !TEMP_PERMISSIONS_TABLE_H

View File

View File

@@ -8,7 +8,10 @@
#include <stddef.h> #include <stddef.h>
#include <sys/types.h> #include <sys/types.h>
#include <time.h>
#define _GNU_SOURCE #define _GNU_SOURCE
#include "cc.h"
#include "temp_permissions_table.h"
#include "ui-socket.h" #include "ui-socket.h"
#include <errno.h> #include <errno.h>
#include <pthread.h> #include <pthread.h>
@@ -19,23 +22,27 @@
#include <sys/un.h> #include <sys/un.h>
#include <unistd.h> #include <unistd.h>
int init_ui_socket(const char *filename) { int init_ui_socket() {
char line[256]; char line[256];
FILE *fp; FILE *fp;
init_temp_permissions_table();
// Test if Zenity is installed (get version) // Test if Zenity is installed (get version)
fp = popen("zenity --version", "r"); fp = popen("zenity --version", "r");
if (fp == NULL) { if (fp == NULL) {
perror("Pipe returned a error"); perror("Pipe returned an error");
return 1; return 1;
} else {
while (fgets(line, sizeof(line), fp))
printf("%s", line);
pclose(fp);
return 0;
} }
while (fgets(line, sizeof(line), fp))
printf("%s", line);
pclose(fp);
return 0;
} }
void destroy_ui_socket() { destroy_temp_permissions_table(); }
/** /**
* Asks the user if the process should be allowed to access the file using the * Asks the user if the process should be allowed to access the file using the
* GUI * GUI
@@ -71,25 +78,19 @@ int ask_access(const char *filename, struct process_info pi) {
// to manually check the output. // to manually check the output.
char buffer[1024]; char buffer[1024];
while (fgets(buffer, sizeof(buffer), fp)) { while (fgets(buffer, sizeof(buffer), fp)) {
if (strcmp(buffer, "Allow this time.\n") == 0) { printf("%s", buffer);
if (strcmp(buffer, "Allow this time\n") == 0) {
pclose(fp); pclose(fp);
return 2; return 2;
} }
} }
int zenity_exit_code = WEXITSTATUS(pclose(fp)); int zenity_exit_code = WEXITSTATUS(pclose(fp));
return zenity_exit_code; // zenity returns 1 on "No" >:(
} if (zenity_exit_code == 0) {
return 1;
}
/**
* Checks if the process has a temporary access to the file.
*
* @param filename: The file that the process is trying to access
* @pram pi: The process information
* @return: 0 if access is denied, 1 if access is allowed
*/
int check_temp_access(const char *filename, struct process_info pi) {
perror("Not implemented");
return 0; return 0;
} }
@@ -104,12 +105,9 @@ int check_perm_access(const char *filename, struct process_info pi) {
perror("Not implemented"); perror("Not implemented");
return 0; return 0;
} }
int give_perm_access(const char *filename, struct process_info pi) {
void give_temp_access(const char *filename, struct process_info pi) {
perror("Not implemented");
}
void give_perm_access(const char *filename, struct process_info pi) {
perror("Not implemented"); perror("Not implemented");
return -1;
} }
/** /**

View File

@@ -13,17 +13,31 @@
#ifndef UI_SOCKET_H #ifndef UI_SOCKET_H
#define UI_SOCKET_H #define UI_SOCKET_H
#include "process_info.h"
#include <sys/types.h> #include <sys/types.h>
struct process_info { /**
pid_t PID; * Initialize the GUI communication.
const char *name; *
uid_t UID; * @return: 0 on success, -1 on faliure.
}; */
int init_ui_socket(void);
// For default socket location, set socket_path = NULL. /**
int init_ui_socket(const char *socket_path); * Close the GUI communication.
*/
void destroy_ui_socket(void);
/**
* Check access according to:
* 1. temporary permission table
* 2. permanent permission table
* 3. user descision
*
* @param filename: The file that the process is trying to access
* @pram pi: The process information
* @return: 0 if access is denied, 1 if access is allowed
*/
int interactive_access(const char *filename, struct process_info pi); int interactive_access(const char *filename, struct process_info pi);
#endif // !UI_SOCKET_H #endif // !UI_SOCKET_H

23
test/mock/zenity Executable file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
# fake-zenity: script that mocks the behavior of zenity based on the ./.fake-zenity-response file
if [[ $1 == "--set-fake-response" ]]; then
#someone knows we are fake :)
echo $2 >~/.fake_zenity_response
else
if [ -f ~/.fake_zenity_response ]; then
FAKE_ZENITY_RESPONSE=$(cat ~/.fake_zenity_response)
if [[ $FAKE_ZENITY_RESPONSE == "yes_tmp" ]]; then
printf "Allow this time\n"
exit 1
elif [[ $FAKE_ZENITY_RESPONSE == "no" ]]; then
exit 1
elif [[ $FAKE_ZENITY_RESPONSE == "yes" ]]; then
exit 0
fi
fi
fi
exit -1 # TODO: call actual zenity here

97
test/test.bash Executable file
View File

@@ -0,0 +1,97 @@
#!/bin/bash
# clean what was left from previous tests
rm -rf ./protected
mkdir protected
touch ./protected/do-not-remove ./protected/should-be-removed ./protected/truth ./protected/perm000 ./protected/perm777 ./protected/this-name-is-wrong
chmod 777 ./protected/perm777 ./protected/perm000
echo "Free code, free world." >./protected/motto
# set up the fake-zenity
PATH="$(realpath ./mock/):$PATH"
# mount the filesystem
echo "Run $(date -u +%Y-%m-%dT%H:%M:%S) "
valgrind -s ../build/icfs -o default_permissions ./protected &
sleep 1
# create files
zenity --set-fake-response no
touch ./protected/should-not-exist 2>/dev/null &&
echo "[ICFS-TEST]: touch can create protected/should-not-exist despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp
touch ./protected/should-exist 2>/dev/null &&
echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: touch cannot create protected/should-exist despite access being permitted!" # OK
# write to files
zenity --set-fake-response no
echo "Linux is a cancer that attaches itself in an intellectual property sense to everything it touches." >./protected/truth 2>/dev/null &&
echo "[ICFS-TEST]: echo can write to protected/lie despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp
echo "Sharing knowledge is the most fundamental act of friendship. Because it is a way you can give something without loosing something." >./protected/truth 2>/dev/null &&
echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: echo cannot write to protected/truth despite access being permitted!" # OK
# Read files
zenity --set-fake-response no
cat ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: cat can read protected/this-only despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp
cat ./protected/motto >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: echo cannot create protected/this-only despite access being permitted!" # "Free code, free world."
# remove files
zenity --set-fake-response no
rm ./protected/do-not-remove >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: rm can unlink protected/do-not-remove despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp
rm ./protected/should-be-removed >/dev/null 2>/dev/null &&
echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: rm cannot unlink protected/should-be-removed despite access being permitted!" # OK
# rename files
zenity --set-fake-response no
mv ./protected/truth ./protected/lie 2>/dev/null &&
echo "[ICFS-TEST]: mv can rename protected/truth despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp
mv ./protected/this-name-is-wrong ./protected/this-name-is-correct 2>/dev/null &&
echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: mv cannot rename should-be-removed to renamed-file despite access being permitted!" # OK
# change permissions
zenity --set-fake-response no
chmod 000 ./protected/perm777 2>/dev/null &&
echo "[ICFS-TEST]: chmod can change permissions of protected/perm777 despite access being denied!" ||
echo "[ICFS-TEST]: OK" # EACCESS
zenity --set-fake-response yes_tmp
chmod 000 ./protected/perm000 2>/dev/null &&
echo "[ICFS-TEST]: OK" ||
echo "[ICFS-TEST]: chmod cannot change permissions of protected/perm000 despite access being permitted!" # OK
# unmount
sleep 0.5
#lsof +f -- $(realpath ./protected)
umount $(realpath ./protected)
sleep 0.5