summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AGENTS.md14
-rw-r--r--ioriot/src/generate/gioop.c28
-rw-r--r--ioriot/src/replay/rioop.c36
-rw-r--r--ioriot/src/replay/rioop.h1
-rw-r--r--scripts/syscall_compat32.c134
-rw-r--r--systemtap/src/ioriot.stp20
-rw-r--r--systemtap/src/javaioriot.stp20
-rw-r--r--systemtap/src/targetedioriot.stp20
8 files changed, 248 insertions, 25 deletions
diff --git a/AGENTS.md b/AGENTS.md
index c519737..bf3f908 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -246,3 +246,17 @@ attempts to exercise these operations:
If the generated replay only contains the header and `#INIT`, or the replay
paths escape the intended test tree, investigate capture tokenization and
dirfd-relative path handling first.
+
+## Legacy Compat Syscalls
+
+The normal helper above exercises the current x86_64 syscall surface. For
+legacy compat names that only show up through the 32-bit ABI on this host,
+use [scripts/syscall_compat32.c](/home/paul/git/ioriot/scripts/syscall_compat32.c)
+and compile it with `cc -m32`.
+
+- It reuses the same `PID_FILE GO_FILE BASE_DIR USER` contract as the main
+ matrix helper, so the capture/generate/init/replay procedure above applies
+ unchanged.
+- It covers `readdir`, `llseek`, `statfs64`, `fstatfs64`, `chown16`,
+ `lchown16`, and `fchown16`.
+- Run it with a user whose uid/gid fit into the legacy 16-bit chown ABI.
diff --git a/ioriot/src/generate/gioop.c b/ioriot/src/generate/gioop.c
index f089a2c..887496b 100644
--- a/ioriot/src/generate/gioop.c
+++ b/ioriot/src/generate/gioop.c
@@ -59,6 +59,15 @@ _gioop_creat_flags(const gtask_s *t)
return O_CREAT | O_WRONLY | O_TRUNC;
}
+static const char*
+_gioop_label(const gtask_s *t, const char *fallback)
+{
+ if (t != NULL && t->op != NULL)
+ return t->op;
+
+ return fallback;
+}
+
static const gioop_entry_s _GIOOP_PREOPEN[] = {
{"open", gioop_open},
{"openat", gioop_openat},
@@ -112,6 +121,7 @@ static const gioop_entry_s _GIOOP_DISPATCH[] = {
{"lchown", gioop_lchown},
{"lchown16", gioop_lchown},
{"fchown", gioop_fchown},
+ {"fchown16", gioop_fchown},
{"fchownat", gioop_chown},
{"exit_group", gioop_exit_group},
};
@@ -138,6 +148,9 @@ void gioop_test(void)
assert(_gioop_creat_flags(&task) == (O_CREAT | O_WRONLY | O_TRUNC));
task.flags = O_RDONLY;
assert(_gioop_creat_flags(&task) == O_RDONLY);
+ task.op = "lchown16";
+ assert(Eq(_gioop_label(&task, "fallback"), "lchown16"));
+ assert(Eq(_gioop_label(NULL, "fallback"), "fallback"));
}
status_e gioop_run(gwriter_s *w, gtask_s *t)
@@ -771,13 +784,16 @@ status_e gioop_fchmod(gwriter_s *w, gtask_s *t, generate_s *g)
status_e gioop_chown(gwriter_s *w, gtask_s *t, generate_s *g)
{
+ const char *label = _gioop_label(t, "chown");
+
if (t->path == NULL) {
return ERROR;
}
generate_vsize_by_path(g, t, NULL);
// Hmm, maybe rename t->offset, because here it is used for the user UID
- Gioop_write(CHOWN, "%s|%ld|%d|%d|chown", t->path, t->offset, t->G, t->status);
+ Gioop_write(CHOWN, "%s|%ld|%d|%d|%s",
+ t->path, t->offset, t->G, t->status, label);
if (t->status == 0)
vsize_stat(t->vsize, t->path);
@@ -787,26 +803,32 @@ status_e gioop_chown(gwriter_s *w, gtask_s *t, generate_s *g)
status_e gioop_fchown(gwriter_s *w, gtask_s *t, generate_s *g)
{
+ const char *label = _gioop_label(t, "fchown");
+
if (!t->has_fd) {
return ERROR;
}
generate_vsize_by_path(g, t, t->vfd->path);
// Hmm, maybe rename t->offset, because here it is used for the user UID
- Gioop_write(FCHOWN, "%ld|%ld|%d|%d|fchown", t->mapped_fd, t->offset, t->G, t->status);
+ Gioop_write(FCHOWN, "%ld|%ld|%d|%d|%s",
+ t->mapped_fd, t->offset, t->G, t->status, label);
return SUCCESS;
}
status_e gioop_lchown(gwriter_s *w, gtask_s *t, generate_s *g)
{
+ const char *label = _gioop_label(t, "lchown");
+
if (t->path == NULL) {
return ERROR;
}
generate_vsize_by_path(g, t, NULL);
// Hmm, maybe rename t->offset, because here it is used for the user UID
- Gioop_write(LCHOWN, "%s|%ld|%d|%d|chown", t->path, t->offset, t->G, t->status);
+ Gioop_write(LCHOWN, "%s|%ld|%d|%d|%s",
+ t->path, t->offset, t->G, t->status, label);
if (t->status == 0)
vsize_stat(t->vsize, t->path);
diff --git a/ioriot/src/replay/rioop.c b/ioriot/src/replay/rioop.c
index 2b3d98c..0d20eff 100644
--- a/ioriot/src/replay/rioop.c
+++ b/ioriot/src/replay/rioop.c
@@ -100,6 +100,19 @@ _rioop_open_creat(rprocess_s *p, rthread_s *t, rtask_s *task)
rioop_open(p, t, task, O_CREAT|O_WRONLY|O_TRUNC);
}
+static DIR*
+_rioop_get_dir_stream(vfd_s *vfd)
+{
+ if (vfd == NULL)
+ return NULL;
+
+ if (vfd->dirfd != NULL)
+ return vfd->dirfd;
+
+ vfd->dirfd = fdopendir(vfd->fd);
+ return vfd->dirfd;
+}
+
static rioop_handler_f
_rioop_find_handler(const rioop_entry_s *entries, const size_t num_entries,
const int op)
@@ -147,14 +160,18 @@ static const rioop_entry_s _RIOOP_HANDLERS[] = {
{SYNC_FILE_RANGE, _rioop_noop},
{FCNTL, rioop_fcntl},
{GETDENTS, rioop_getdents},
+ {READDIR, rioop_readdir},
{LSEEK, rioop_lseek},
{LLSEEK, rioop_lseek},
{CHMOD, rioop_chmod},
{FCHMOD, rioop_fchmod},
{CHOWN, rioop_chown},
+ {CHOWN16, rioop_chown},
{FCHOWN, rioop_fchown},
+ {FCHOWN16, rioop_fchown},
{FCHOWNAT, rioop_fchown},
{LCHOWN, rioop_lchown},
+ {LCOWN16, rioop_lchown},
{META_EXIT_GROUP, _rioop_noop},
{META_TIMELINE, _rioop_noop},
};
@@ -169,6 +186,8 @@ void rioop_test(void)
_rioop_open_creat);
assert(_rioop_find_handler(_RIOOP_HANDLERS, handler_count, READAHEAD) ==
_rioop_noop);
+ assert(_rioop_find_handler(_RIOOP_HANDLERS, handler_count, READDIR) ==
+ rioop_readdir);
assert(_rioop_find_handler(_RIOOP_HANDLERS, handler_count, -1) == NULL);
}
@@ -320,10 +339,9 @@ void rioop_getdents(rprocess_s *p, rthread_s *t, rtask_s *task)
_Init_fd(3);
_Init_virtfd;
- // getdents expects a dirfd
- DIR *dirfd = fdopendir(vfd->fd);
- if (dirfd) {
- vfd->dirfd = dirfd;
+ // Directory readers reuse the same DIR* stream so offsets stay aligned.
+ DIR *dirfd = _rioop_get_dir_stream(vfd);
+ if (dirfd != NULL) {
readdir(dirfd);
#ifdef THREAD_DEBUG
fprintf(t->rthread_fd, "TRACE OPEN|fdopendir|%s|\n", vfd->path);
@@ -332,6 +350,16 @@ void rioop_getdents(rprocess_s *p, rthread_s *t, rtask_s *task)
}
}
+void rioop_readdir(rprocess_s *p, rthread_s *t, rtask_s *task)
+{
+ _Init_fd(3);
+ _Init_virtfd;
+
+ DIR *dirfd = _rioop_get_dir_stream(vfd);
+ if (dirfd != NULL)
+ readdir(dirfd);
+}
+
void rioop_mkdir(rprocess_s *p, rthread_s *t, rtask_s *task)
{
_Init_path(3);
diff --git a/ioriot/src/replay/rioop.h b/ioriot/src/replay/rioop.h
index aff2a21..5d0db2b 100644
--- a/ioriot/src/replay/rioop.h
+++ b/ioriot/src/replay/rioop.h
@@ -40,6 +40,7 @@ void rioop_getdents(rprocess_s *p, rthread_s *t, rtask_s *task);
void rioop_mkdir(rprocess_s *p, rthread_s *t, rtask_s *task);
void rioop_open(rprocess_s *p, rthread_s *t, rtask_s *task, int flags_);
void rioop_read(rprocess_s *p, rthread_s *t, rtask_s *task);
+void rioop_readdir(rprocess_s *p, rthread_s *t, rtask_s *task);
void rioop_rename(rprocess_s *p, rthread_s *t, rtask_s *task);
void rioop_stat(rprocess_s *p, rthread_s *t, rtask_s *task);
void rioop_lseek(rprocess_s *p, rthread_s *t, rtask_s *task);
diff --git a/scripts/syscall_compat32.c b/scripts/syscall_compat32.c
new file mode 100644
index 0000000..03a3474
--- /dev/null
+++ b/scripts/syscall_compat32.c
@@ -0,0 +1,134 @@
+#define _GNU_SOURCE
+#define _LARGEFILE64_SOURCE
+
+#include <asm/unistd_32.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#if !defined(__i386__)
+#error "Compile this helper with -m32 so it exercises the compat syscall ABI."
+#endif
+
+static void
+die(const char *msg)
+{
+ perror(msg);
+ exit(1);
+}
+
+static void
+must(bool ok, const char *msg)
+{
+ if (!ok)
+ die(msg);
+}
+
+static void
+must_eq(long rc, const char *msg)
+{
+ if (rc == -1)
+ die(msg);
+}
+
+static void
+write_pid_file(const char *path)
+{
+ FILE *fp = fopen(path, "w");
+
+ must(fp != NULL, "fopen pid file");
+ fprintf(fp, "%ld\n", (long)getpid());
+ fclose(fp);
+}
+
+static void
+wait_for_go(const char *path)
+{
+ while (access(path, F_OK) != 0)
+ usleep(10000);
+}
+
+static void
+write_full(int fd, const char *buf, size_t len)
+{
+ while (len > 0) {
+ ssize_t written = write(fd, buf, len);
+
+ must_eq(written, "write");
+ buf += written;
+ len -= (size_t)written;
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ if (argc != 5) {
+ fprintf(stderr, "usage: %s PID_FILE GO_FILE BASE_DIR USER\n", argv[0]);
+ return 2;
+ }
+
+ const char *pid_file = argv[1];
+ const char *go_file = argv[2];
+ const char *base = argv[3];
+ const char *user = argv[4];
+ struct passwd *pw = getpwnam(user);
+
+ must(pw != NULL, "getpwnam");
+ must(pw->pw_uid <= USHRT_MAX, "uid fits 16-bit compat ABI");
+ must(pw->pw_gid <= USHRT_MAX, "gid fits 16-bit compat ABI");
+
+ write_pid_file(pid_file);
+ wait_for_go(go_file);
+
+ char path_file[1024];
+ char path_link[1024];
+
+ snprintf(path_file, sizeof(path_file), "%s/compat-open.txt", base);
+ snprintf(path_link, sizeof(path_link), "%s/compat-link.txt", base);
+
+ must_eq(mkdir(base, 0777), "mkdir compat base");
+
+ int dirfd = open(base, O_RDONLY | O_DIRECTORY);
+
+ must_eq(dirfd, "open compat base");
+
+ int fd = open(path_file, O_CREAT | O_RDWR | O_TRUNC, 0644);
+
+ must_eq(fd, "open compat-open.txt");
+ write_full(fd, "compat-data", 11);
+ must_eq(lseek(fd, 0, SEEK_SET), "lseek compat-open.txt");
+ must_eq(symlink(path_file, path_link), "symlink compat-link.txt");
+
+ char dir_buf[4096];
+ long long llseek_result = 0;
+ struct statfs64 sfs;
+
+ must_eq(syscall(__NR_readdir, dirfd, dir_buf, sizeof(dir_buf)),
+ "compat readdir");
+ must_eq(syscall(__NR__llseek, fd, 0UL, 0UL, &llseek_result, SEEK_END),
+ "compat _llseek");
+ must_eq(syscall(__NR_statfs64, base, sizeof(sfs), &sfs),
+ "compat statfs64");
+ must_eq(syscall(__NR_fstatfs64, fd, sizeof(sfs), &sfs),
+ "compat fstatfs64");
+ must_eq(syscall(__NR_chown, path_file, pw->pw_uid, pw->pw_gid),
+ "compat chown16");
+ must_eq(syscall(__NR_lchown, path_link, pw->pw_uid, pw->pw_gid),
+ "compat lchown16");
+ must_eq(syscall(__NR_fchown, fd, pw->pw_uid, pw->pw_gid),
+ "compat fchown16");
+
+ must_eq(close(fd), "close compat-open.txt");
+ must_eq(close(dirfd), "close compat base");
+
+ return 0;
+}
diff --git a/systemtap/src/ioriot.stp b/systemtap/src/ioriot.stp
index 9bdf440..9103a25 100644
--- a/systemtap/src/ioriot.stp
+++ b/systemtap/src/ioriot.stp
@@ -944,9 +944,12 @@ probe syscall.fchmod.return {
# --- chown/lchown ---
# Tapset entry vars: path_unquoted, owner, group
-# Note: chown16/lchown16 do not exist on x86_64
+# Compat note: 64-bit workloads use chown/lchown, while 32-bit compat
+# workloads may emit chown16/lchown16 on the same kernel.
probe syscall.chown,
- syscall.lchown {
+ syscall.chown16,
+ syscall.lchown,
+ syscall.lchown16 {
if (execname() != "stapio") {
PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns()
ENTRY_PATH[tid(),name] = path_unquoted
@@ -956,7 +959,9 @@ probe syscall.chown,
}
probe syscall.chown.return,
- syscall.lchown.return {
+ syscall.chown16.return,
+ syscall.lchown.return,
+ syscall.lchown16.return {
if(execname() != "stapio") {
ns = gettimeofday_ns()
printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,p=%s;:,O=%d;:,G=%d;:,s=%d;:,\n",
@@ -975,8 +980,10 @@ probe syscall.chown.return,
# --- fchown ---
# Tapset entry vars: fd, owner, group
-# Note: fchown16 does not exist on x86_64
-probe syscall.fchown {
+# Compat note: 64-bit workloads use fchown, while 32-bit compat workloads may
+# emit fchown16 on the same kernel.
+probe syscall.fchown,
+ syscall.fchown16 {
if (execname() != "stapio") {
PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns()
ENTRY_FD[tid(),name] = fd
@@ -985,7 +992,8 @@ probe syscall.fchown {
}
}
-probe syscall.fchown.return {
+probe syscall.fchown.return,
+ syscall.fchown16.return {
if(execname() != "stapio") {
ns = gettimeofday_ns()
printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,d=%ld;:,O=%d;:,G=%d;:,s=%d;:,\n",
diff --git a/systemtap/src/javaioriot.stp b/systemtap/src/javaioriot.stp
index cf7e797..0c59bf2 100644
--- a/systemtap/src/javaioriot.stp
+++ b/systemtap/src/javaioriot.stp
@@ -944,9 +944,12 @@ probe syscall.fchmod.return {
# --- chown/lchown ---
# Tapset entry vars: path_unquoted, owner, group
-# Note: chown16/lchown16 do not exist on x86_64
+# Compat note: 64-bit workloads use chown/lchown, while 32-bit compat
+# workloads may emit chown16/lchown16 on the same kernel.
probe syscall.chown,
- syscall.lchown {
+ syscall.chown16,
+ syscall.lchown,
+ syscall.lchown16 {
if (execname() == "java") {
PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns()
ENTRY_PATH[tid(),name] = path_unquoted
@@ -956,7 +959,9 @@ probe syscall.chown,
}
probe syscall.chown.return,
- syscall.lchown.return {
+ syscall.chown16.return,
+ syscall.lchown.return,
+ syscall.lchown16.return {
if(execname() == "java") {
ns = gettimeofday_ns()
printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,p=%s;:,O=%d;:,G=%d;:,s=%d;:,\n",
@@ -975,8 +980,10 @@ probe syscall.chown.return,
# --- fchown ---
# Tapset entry vars: fd, owner, group
-# Note: fchown16 does not exist on x86_64
-probe syscall.fchown {
+# Compat note: 64-bit workloads use fchown, while 32-bit compat workloads may
+# emit fchown16 on the same kernel.
+probe syscall.fchown,
+ syscall.fchown16 {
if (execname() == "java") {
PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns()
ENTRY_FD[tid(),name] = fd
@@ -985,7 +992,8 @@ probe syscall.fchown {
}
}
-probe syscall.fchown.return {
+probe syscall.fchown.return,
+ syscall.fchown16.return {
if(execname() == "java") {
ns = gettimeofday_ns()
printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,d=%ld;:,O=%d;:,G=%d;:,s=%d;:,\n",
diff --git a/systemtap/src/targetedioriot.stp b/systemtap/src/targetedioriot.stp
index dccfe4c..7253b65 100644
--- a/systemtap/src/targetedioriot.stp
+++ b/systemtap/src/targetedioriot.stp
@@ -944,9 +944,12 @@ probe syscall.fchmod.return {
# --- chown/lchown ---
# Tapset entry vars: path_unquoted, owner, group
-# Note: chown16/lchown16 do not exist on x86_64
+# Compat note: 64-bit workloads use chown/lchown, while 32-bit compat
+# workloads may emit chown16/lchown16 on the same kernel.
probe syscall.chown,
- syscall.lchown {
+ syscall.chown16,
+ syscall.lchown,
+ syscall.lchown16 {
if (pid() == target()) {
PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns()
ENTRY_PATH[tid(),name] = path_unquoted
@@ -956,7 +959,9 @@ probe syscall.chown,
}
probe syscall.chown.return,
- syscall.lchown.return {
+ syscall.chown16.return,
+ syscall.lchown.return,
+ syscall.lchown16.return {
if(pid() == target()) {
ns = gettimeofday_ns()
printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,p=%s;:,O=%d;:,G=%d;:,s=%d;:,\n",
@@ -975,8 +980,10 @@ probe syscall.chown.return,
# --- fchown ---
# Tapset entry vars: fd, owner, group
-# Note: fchown16 does not exist on x86_64
-probe syscall.fchown {
+# Compat note: 64-bit workloads use fchown, while 32-bit compat workloads may
+# emit fchown16 on the same kernel.
+probe syscall.fchown,
+ syscall.fchown16 {
if (pid() == target()) {
PROBE_ENTRY_TIMES[tid(),name] = gettimeofday_ns()
ENTRY_FD[tid(),name] = fd
@@ -985,7 +992,8 @@ probe syscall.fchown {
}
}
-probe syscall.fchown.return {
+probe syscall.fchown.return,
+ syscall.fchown16.return {
if(pid() == target()) {
ns = gettimeofday_ns()
printf("t=%ld;:,D=%ld;:,i=%d:%d;:,o=%s;:,d=%ld;:,O=%d;:,G=%d;:,s=%d;:,\n",