Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find usermountN and set FUSERMOUNT_PROG if not set #82

Merged
merged 4 commits into from
Nov 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 42 additions & 67 deletions patches/libfuse/mount.c.diff
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/lib/mount.c b/lib/mount.c
index d71e6fc55..acc1711ff 100644
index d71e6fc..ab3f54b 100644
--- a/lib/mount.c
+++ b/lib/mount.c
@@ -41,7 +41,6 @@
Expand All @@ -10,121 +10,96 @@ index d71e6fc55..acc1711ff 100644
#define FUSE_COMMFD_ENV "_FUSE_COMMFD"

#ifndef HAVE_FORK
@@ -117,17 +116,79 @@ static const struct fuse_opt fuse_mount_opts[] = {
@@ -117,17 +116,44 @@ static const struct fuse_opt fuse_mount_opts[] = {
FUSE_OPT_END
};

+int fileExists(const char* path);
+char* findBinaryInFusermountDir(const char* binaryName);
+
+int fileExists(const char* path) {
+ FILE* file = fopen(path, "r");
+ if (file) {
+ fclose(file);
+ return 1;
+ }
+ return 0;
+}
+
+char* findBinaryInFusermountDir(const char* binaryName) {
+ // For security reasons, we do not search the binary on the $PATH;
+ // instead, we check if the binary exists in FUSERMOUNT_DIR
+ // as defined in meson.build
+ char* binaryPath = malloc(strlen(FUSERMOUNT_DIR) + strlen(binaryName) + 2);
+ strcpy(binaryPath, FUSERMOUNT_DIR);
+ strcat(binaryPath, "/");
+ strcat(binaryPath, binaryName);
+ if (fileExists(binaryPath)) {
+ return binaryPath;
+ }
+
+ // If the binary does not exist in FUSERMOUNT_DIR, return NULL
+ return NULL;
+}
+
+static const char *fuse_mount_prog(void)
+/**
+ * Returns FUSERMOUNT_PROG path from environment variable.
+ *
+ * If $FUSERMOUNT_PROG is not set, the program exits.
+ *
+ * Call with FUSERMOUNT_PROG_DEBUG=nonzerovalue to make the code print the value before evaluation.
+ */
+static const char *fusermountProg(void)
+{
+ // Check if the FUSERMOUNT_PROG environment variable is set and if so, use it
+ const char *prog = getenv("FUSERMOUNT_PROG");
+ if (prog) {
+ if (access(prog, X_OK) == 0)
+ return prog;
+ }
+ static const char envVar[] = "FUSERMOUNT_PROG";
+ char *fusermountProg = getenv(envVar);
+
+ // Check if there is a binary "fusermount3"
+ prog = findBinaryInFusermountDir("fusermount3");
+ if (access(prog, X_OK) == 0)
+ return prog;
+ static const char debugEnvVarSuffix[] = "_DEBUG";
+ char debugEnvVar[sizeof(envVar) + sizeof(debugEnvVarSuffix) + 1];
+ sprintf(debugEnvVar, "%s%s", envVar, debugEnvVarSuffix);
+
+ // Check if there is a binary called "fusermount"
+ // This is known to work for our purposes
+ prog = findBinaryInFusermountDir("fusermount");
+ if (access(prog, X_OK) == 0)
+ return prog;
+ if (fusermountProg == NULL) {
+ fprintf(stderr, "Error: $%s not set\n", envVar);
+ exit(1);
+ }
+
+ // For i = 4...99, check if there is a binary called "fusermount" + i
+ // It is not yet known whether this will work for our purposes, but it is better than not even attempting
+ for (int i = 4; i < 100; i++) {
+ prog = findBinaryInFusermountDir("fusermount" + i);
+ if (access(prog, X_OK) == 0)
+ return prog;
+ if (getenv(debugEnvVar) != NULL) {
+ fprintf(stderr, "$%s: %s\n", envVar, fusermountProg);
+ }
+
+ // If all else fails, return NULL
+ return NULL;
+ return fusermountProg;
+}
+
static void exec_fusermount(const char *argv[])
{
- execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
- execvp(FUSERMOUNT_PROG, (char **) argv);
+ const char *fusermount_prog = fuse_mount_prog();
+ if (fusermount_prog) {
+ execv(fusermount_prog, (char **) argv);
+ }
+ execv(fusermountProg(), (char **) argv);
}

void fuse_mount_version(void)
{
int pid = fork();
if (!pid) {
- const char *argv[] = { FUSERMOUNT_PROG, "--version", NULL };
+ const char *argv[] = { fuse_mount_prog(), "--version", NULL };
+ const char *argv[] = { fusermountProg(), "--version", NULL };
exec_fusermount(argv);
_exit(1);
} else if (pid != -1)
@@ -300,7 +361,7 @@ void fuse_kern_unmount(const char *mountpoint, int fd)
@@ -300,7 +326,7 @@ void fuse_kern_unmount(const char *mountpoint, int fd)
return;

if(pid == 0) {
- const char *argv[] = { FUSERMOUNT_PROG, "-u", "-q", "-z",
+ const char *argv[] = { fuse_mount_prog(), "-u", "-q", "-z",
+ const char *argv[] = { fusermountProg(), "-u", "-q", "-z",
"--", mountpoint, NULL };

exec_fusermount(argv);
@@ -346,7 +407,7 @@ static int setup_auto_unmount(const char *mountpoint, int quiet)
@@ -346,7 +372,7 @@ static int setup_auto_unmount(const char *mountpoint, int quiet)
}
}

- argv[a++] = FUSERMOUNT_PROG;
+ argv[a++] = fuse_mount_prog();
+ argv[a++] = fusermountProg();
argv[a++] = "--auto-unmount";
argv[a++] = "--";
argv[a++] = mountpoint;
@@ -407,7 +468,7 @@ static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
@@ -357,7 +383,7 @@ static int setup_auto_unmount(const char *mountpoint, int quiet)
snprintf(env, sizeof(env), "%i", fds[0]);
setenv(FUSE_COMMFD_ENV, env, 1);
exec_fusermount(argv);
- perror("fuse: failed to exec fusermount3");
+ perror("fuse: failed to exec $FUSERMOUNT_PROG");
_exit(1);
}

@@ -407,7 +433,7 @@ static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
}
}

- argv[a++] = FUSERMOUNT_PROG;
+ argv[a++] = fuse_mount_prog();
+ argv[a++] = fusermountProg();
if (opts) {
argv[a++] = "-o";
argv[a++] = opts;
@@ -421,7 +482,7 @@ static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
@@ -421,7 +447,7 @@ static int fuse_mount_fusermount(const char *mountpoint, struct mount_opts *mo,
snprintf(env, sizeof(env), "%i", fds[0]);
setenv(FUSE_COMMFD_ENV, env, 1);
exec_fusermount(argv);
- perror("fuse: failed to exec fusermount3");
+ perror("fuse: failed to exec fusermount");
+ perror("fuse: failed to exec $FUSERMOUNT_PROG");
_exit(1);
}

124 changes: 118 additions & 6 deletions src/runtime/runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* as possible (one .c file) and use as few external dependencies
* as possible
*
* Copyright (c) 2004-22 Simon Peter
* Copyright (c) 2004-24 Simon Peter
* Portions Copyright (c) 2007 Alexander Larsson
* Portions from WjCryptLib_Md5 originally written by Alexander Peslyak,
modified by WaterJuice retaining Public Domain license
Expand Down Expand Up @@ -62,6 +62,10 @@ extern int sqfs_opt_proc(void* data, const char* arg, int key, struct fuse_args*
#include <sys/mman.h>
#include <stdint.h>
#include <libgen.h>
#include <dirent.h>
#include <ctype.h>

const char* fusermountPath = NULL;

typedef struct {
uint32_t lo;
Expand Down Expand Up @@ -405,6 +409,99 @@ int appimage_print_binary(char* fname, unsigned long offset, unsigned long lengt
return 0;
}

char* find_fusermount(bool verbose) {
char* fusermount_base = "fusermount";

char* fusermount_path = getenv("PATH");
if (fusermount_path == NULL) {
return NULL;
}

char* path_copy = strdup(fusermount_path);
char* dir = strtok(path_copy, ":");

while (dir != NULL) {
DIR* dir_ptr = opendir(dir);
if (dir_ptr == NULL) {
dir = strtok(NULL, ":");
continue;
}

struct dirent* entry;
while ((entry = readdir(dir_ptr)) != NULL) {
// Check if the entry starts with "fusermount"
if (strncmp(entry->d_name, fusermount_base, 10) == 0) {
// Check if the rest of the entry is a digit
char* suffix = entry->d_name + 10;
int j = 0;
while (suffix[j] != '\0' && isdigit(suffix[j])) {
j++;
}

if (suffix[j] == '\0') {
// Construct the full path of the entry
char* fusermount_full_path = malloc(strlen(dir) + strlen(entry->d_name) + 2);
sprintf(fusermount_full_path, "%s/%s", dir, entry->d_name);

// Check if the binary is setuid root
struct stat sb;
if (stat(fusermount_full_path, &sb) == -1) {
perror("stat");
free(fusermount_full_path);
continue;
}

if (sb.st_uid != 0 || (sb.st_mode & S_ISUID) == 0) {
if (verbose) {
printf("Not setuid root, skipping...\n");
}
free(fusermount_full_path);
continue;
}

if (verbose) {
printf("Found setuid root executable: %s\n", fusermount_full_path);
}

pid_t pid = fork();
if (pid == -1) {
perror("fork");
free(fusermount_full_path);
continue;
}

if (pid == 0) {
// Child process
char* args[] = {fusermount_full_path, "--version", NULL};
execvp(fusermount_full_path, args);
// If execvp returns, it means the executable was not found
exit(1);
} else {
// Parent process
int status;
waitpid(pid, &status, 0);

if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
// The executable was found and executed successfully
closedir(dir_ptr);
free(path_copy);
return fusermount_full_path;
}

free(fusermount_full_path);
}
}
}
}

closedir(dir_ptr);
dir = strtok(NULL, ":");
}

free(path_copy);
return NULL;
}

/* Exit status to use when launching an AppImage fails.
* For applications that assign meanings to exit status codes (e.g. rsync),
* we avoid "cluttering" pre-defined exit status codes by using 127 which
Expand Down Expand Up @@ -1330,6 +1427,11 @@ char* appimage_hexlify(const char* bytes, const size_t numBytes) {
}

int main(int argc, char* argv[]) {
const bool verbose = (getenv("VERBOSE") != NULL);
if (verbose) {
fprintf(stderr, "Running in verbose mode\n");
}

char appimage_path[PATH_MAX];
char argv0_path[PATH_MAX];
char* arg;
Expand Down Expand Up @@ -1410,8 +1512,6 @@ int main(int argc, char* argv[]) {
exit(1);
}

const bool verbose = (getenv("VERBOSE") != NULL);

if (!extract_appimage(appimage_path, "squashfs-root/", pattern, true, verbose)) {
exit(1);
}
Expand Down Expand Up @@ -1473,8 +1573,6 @@ int main(int argc, char* argv[]) {
strcat(prefix, hexlified_digest);
free(hexlified_digest);

const bool verbose = (getenv("VERBOSE") != NULL);

if (!extract_appimage(appimage_path, prefix, NULL, false, verbose)) {
fprintf(stderr, "Failed to extract AppImage\n");
exit(EXIT_EXECERROR);
Expand Down Expand Up @@ -1556,7 +1654,7 @@ int main(int argc, char* argv[]) {
appimage_print_binary(appimage_path, offset, length);
exit(0);
}

portable_option(arg, appimage_path, "home");
portable_option(arg, appimage_path, "config");

Expand Down Expand Up @@ -1600,6 +1698,20 @@ int main(int argc, char* argv[]) {
if (pid == 0) {
/* in child */

fusermountPath = getenv("FUSERMOUNT_PROG");
if (fusermountPath == NULL) {
char* new_prog = find_fusermount(verbose);
if (new_prog != NULL) {
setenv("FUSERMOUNT_PROG", new_prog, 1);
if (verbose) {
fprintf(stderr, "FUSERMOUNT_PROG set to %s\n", new_prog);
}
free(new_prog);
} else {
printf("Error: No suitable fusermount binary found on the $PATH\n");
}
}

char* child_argv[5];

/* close read pipe */
Expand Down