Skip to content

Commit d46096e

Browse files
ehfdthelamer
authored andcommitted
Merge pull request #246 from selkies-project/interposer-fixes
fix 000 file perms, implement openat (#247) * loop tested input, only fall back when needed by checking if shift modifier is needed for english keyboards * overhaul api spec * fix manual scaling in shared mode * watch all res changes on shared client to adjust canvas rendering * Revert "watch all res changes on shared client to adjust canvas rendering" This reverts commit 45e8bef. * Revert "fix manual scaling in shared mode" * fix dpr conversion * clamp to only allow x264enc when shared mode is enabled stop supporting striped modes for shared clients and controllers * bad var * ripped out too much * formatting and use display width and height * load libxkb for fallback to handle cyrillic characters * track if an action modifier is being used for cyrillic keys and perform qwerty action * fix 000 file perms, implement openat --------- Co-authored-by: thelamer <ryankuba@gmail.com>
1 parent 14ad9a5 commit d46096e

4 files changed

Lines changed: 445 additions & 400 deletions

File tree

addons/js-interposer/joystick_interposer.c

Lines changed: 148 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,20 @@ file, You can obtain one at https://mozilla.org/MPL/2.0/.
3737
#include <linux/input.h>
3838
#include <linux/input-event-codes.h>
3939

40+
/**
41+
* @brief Definitions for O_TMPFILE and mode requirement checking.
42+
*
43+
* O_TMPFILE allows creating unnamed temporary files, which requires a third
44+
* 'mode' argument just like O_CREAT. The NEEDS_MODE macro safely identifies
45+
* if the flags passed to open/openat require extracting this mode argument
46+
* from the variadic list to prevent creating files with 000 permissions.
47+
*/
48+
#ifndef O_TMPFILE
49+
#define __O_TMPFILE 020000000
50+
#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY)
51+
#endif
52+
#define NEEDS_MODE(flags) (((flags) & O_CREAT) || (((flags) & O_TMPFILE) == O_TMPFILE))
53+
4054
/**
4155
* @brief Defines the data type for ioctl request codes.
4256
*
@@ -134,6 +148,8 @@ static int g_sji_log_enabled = 0;
134148
*/
135149
static int (*real_open)(const char *pathname, int flags, ...) = NULL;
136150
static int (*real_open64)(const char *pathname, int flags, ...) = NULL;
151+
static int (*real_openat)(int dirfd, const char *pathname, int flags, ...) = NULL;
152+
static int (*real_openat64)(int dirfd, const char *pathname, int flags, ...) = NULL;
137153
static int (*real_ioctl)(int fd, ioctl_request_t request, ...) = NULL;
138154
static int (*real_epoll_ctl)(int epfd, int op, int fd, struct epoll_event *event) = NULL;
139155
static int (*real_close)(int fd) = NULL;
@@ -414,6 +430,8 @@ __attribute__((constructor)) void init_interposer() {
414430
if (load_real_func((void *)&real_stat, "stat") < 0) sji_log_error("CRITICAL: Failed to load real 'stat'.");
415431
if (load_real_func((void *)&real_lstat, "lstat") < 0) sji_log_error("CRITICAL: Failed to load real 'lstat'.");
416432
load_real_func((void *)&real_open64, "open64");
433+
load_real_func((void *)&real_openat, "openat");
434+
load_real_func((void *)&real_openat64, "openat64");
417435
sji_log_info("Selkies Joystick Interposer initialized. Logging is %s.", g_sji_log_enabled ? "ENABLED" : "DISABLED");
418436
}
419437

@@ -821,7 +839,6 @@ static int common_open_logic(const char *pathname, int flags, js_interposer_t **
821839
*/
822840
int open(const char *pathname, int flags, ...) {
823841
if (!real_open) {
824-
sji_log_error("CRITICAL: real_open not loaded. Cannot proceed with open call.");
825842
errno = EFAULT;
826843
return -1;
827844
}
@@ -830,11 +847,10 @@ int open(const char *pathname, int flags, ...) {
830847
int result_fd = common_open_logic(pathname, flags, &interposer);
831848

832849
if (result_fd == -2) {
833-
mode_t mode = 0;
834-
if (flags & O_CREAT) {
850+
if (NEEDS_MODE(flags)) {
835851
va_list args;
836852
va_start(args, flags);
837-
mode = va_arg(args, mode_t);
853+
mode_t mode = va_arg(args, mode_t);
838854
va_end(args);
839855
result_fd = real_open(pathname, flags, mode);
840856
} else {
@@ -847,6 +863,7 @@ int open(const char *pathname, int flags, ...) {
847863
#ifdef open64
848864
#undef open64
849865
#endif
866+
850867
/**
851868
* @brief Intercepted `open64()` system call.
852869
*
@@ -864,7 +881,6 @@ int open(const char *pathname, int flags, ...) {
864881
*/
865882
int open64(const char *pathname, int flags, ...) {
866883
if (!real_open64 && !real_open) {
867-
sji_log_error("CRITICAL: Neither real_open64 nor real_open loaded. Cannot proceed with open64 call.");
868884
errno = EFAULT;
869885
return -1;
870886
}
@@ -873,19 +889,139 @@ int open64(const char *pathname, int flags, ...) {
873889
int result_fd = common_open_logic(pathname, flags, &interposer);
874890

875891
if (result_fd == -2) {
876-
mode_t mode = 0;
877-
if (flags & O_CREAT) {
892+
if (NEEDS_MODE(flags)) {
893+
va_list args;
894+
va_start(args, flags);
895+
mode_t mode = va_arg(args, mode_t);
896+
va_end(args);
897+
898+
if (real_open64) {
899+
result_fd = real_open64(pathname, flags, mode);
900+
} else {
901+
result_fd = real_open(pathname, flags, mode);
902+
}
903+
} else {
904+
if (real_open64) {
905+
result_fd = real_open64(pathname, flags);
906+
} else {
907+
result_fd = real_open(pathname, flags);
908+
}
909+
}
910+
}
911+
return result_fd;
912+
}
913+
914+
/**
915+
* @brief Intercepted `openat()` system call.
916+
*
917+
* Resolves the full path if a relative path and directory fd are provided.
918+
* Uses `common_open_logic()` to handle interposition for target device paths.
919+
* Safely extracts and passes the `mode` argument if file creation flags
920+
* (O_CREAT or O_TMPFILE) are present to prevent permission bugs.
921+
*
922+
* @param dirfd The directory file descriptor.
923+
* @param pathname The path to the file to open.
924+
* @param flags Flags for opening the file.
925+
* @param ... Optional `mode_t mode` argument if file creation flags are set.
926+
* @return A file descriptor on success, or -1 on error (`errno` is set).
927+
*/
928+
int openat(int dirfd, const char *pathname, int flags, ...) {
929+
if (!real_openat) {
930+
errno = EFAULT;
931+
return -1;
932+
}
933+
934+
char full_path[4096];
935+
const char *check_path = pathname;
936+
937+
if (pathname && pathname[0] != '/' && dirfd != AT_FDCWD) {
938+
char procfd[64];
939+
snprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", dirfd);
940+
ssize_t len = readlink(procfd, full_path, sizeof(full_path) - 1);
941+
if (len != -1) {
942+
full_path[len] = '/';
943+
strncpy(full_path + len + 1, pathname, sizeof(full_path) - len - 2);
944+
full_path[sizeof(full_path) - 1] = '\0';
945+
check_path = full_path;
946+
}
947+
}
948+
949+
js_interposer_t *interposer = NULL;
950+
int result_fd = common_open_logic(check_path, flags, &interposer);
951+
952+
if (result_fd == -2) {
953+
if (NEEDS_MODE(flags)) {
878954
va_list args;
879955
va_start(args, flags);
880-
mode = va_arg(args, mode_t);
956+
mode_t mode = va_arg(args, mode_t);
881957
va_end(args);
958+
result_fd = real_openat(dirfd, pathname, flags, mode);
959+
} else {
960+
result_fd = real_openat(dirfd, pathname, flags);
961+
}
962+
}
963+
return result_fd;
964+
}
965+
966+
#ifdef openat64
967+
#undef openat64
968+
#endif
969+
970+
/**
971+
* @brief Intercepted `openat64()` system call.
972+
*
973+
* 64-bit variant of the intercepted `openat()` system call. Resolves relative
974+
* paths, applies interposer logic, and safely handles variadic `mode` arguments.
975+
* Falls back to `real_openat()` if `real_openat64` is not available.
976+
*
977+
* @param dirfd The directory file descriptor.
978+
* @param pathname The path to the file to open.
979+
* @param flags Flags for opening the file.
980+
* @param ... Optional `mode_t mode` argument if file creation flags are set.
981+
* @return A file descriptor on success, or -1 on error (`errno` is set).
982+
*/
983+
int openat64(int dirfd, const char *pathname, int flags, ...) {
984+
if (!real_openat64 && !real_openat) {
985+
errno = EFAULT;
986+
return -1;
987+
}
988+
989+
char full_path[4096];
990+
const char *check_path = pathname;
991+
992+
if (pathname && pathname[0] != '/' && dirfd != AT_FDCWD) {
993+
char procfd[64];
994+
snprintf(procfd, sizeof(procfd), "/proc/self/fd/%d", dirfd);
995+
ssize_t len = readlink(procfd, full_path, sizeof(full_path) - 1);
996+
if (len != -1) {
997+
full_path[len] = '/';
998+
strncpy(full_path + len + 1, pathname, sizeof(full_path) - len - 2);
999+
full_path[sizeof(full_path) - 1] = '\0';
1000+
check_path = full_path;
8821001
}
1002+
}
1003+
1004+
js_interposer_t *interposer = NULL;
1005+
int result_fd = common_open_logic(check_path, flags, &interposer);
8831006

884-
if (real_open64) {
885-
result_fd = (flags & O_CREAT) ? real_open64(pathname, flags, mode) : real_open64(pathname, flags);
1007+
if (result_fd == -2) {
1008+
if (NEEDS_MODE(flags)) {
1009+
va_list args;
1010+
va_start(args, flags);
1011+
mode_t mode = va_arg(args, mode_t);
1012+
va_end(args);
1013+
1014+
if (real_openat64) {
1015+
result_fd = real_openat64(dirfd, pathname, flags, mode);
1016+
} else {
1017+
result_fd = real_openat(dirfd, pathname, flags, mode);
1018+
}
8861019
} else {
887-
sji_log_info("real_open64 not available, falling back to real_open for: %s", pathname);
888-
result_fd = (flags & O_CREAT) ? real_open(pathname, flags, mode) : real_open(pathname, flags);
1020+
if (real_openat64) {
1021+
result_fd = real_openat64(dirfd, pathname, flags);
1022+
} else {
1023+
result_fd = real_openat(dirfd, pathname, flags);
1024+
}
8891025
}
8901026
}
8911027
return result_fd;

0 commit comments

Comments
 (0)