From: David Matlack <dmatlack@google.com>
To: kexec@lists.infradead.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-kselftest@vger.kernel.org,
linux-mm@kvack.org
Cc: Alexander Graf <graf@amazon.com>,
Andrew Morton <akpm@linux-foundation.org>,
Baolin Wang <baolin.wang@linux.alibaba.com>,
Christian Brauner <brauner@kernel.org>,
David Hildenbrand <david@kernel.org>,
Hugh Dickins <hughd@google.com>, Jonathan Corbet <corbet@lwn.net>,
"Liam R. Howlett" <liam@infradead.org>,
Lorenzo Stoakes <ljs@kernel.org>, Michal Hocko <mhocko@suse.com>,
Mike Rapoport <rppt@kernel.org>,
Pasha Tatashin <pasha.tatashin@soleen.com>,
Pratyush Yadav <pratyush@kernel.org>,
Randy Dunlap <rdunlap@infradead.org>,
Samiullah Khawaja <skhawaja@google.com>,
Shuah Khan <skhan@linuxfoundation.org>,
Suren Baghdasaryan <surenb@google.com>,
Vlastimil Babka <vbabka@kernel.org>,
David Matlack <dmatlack@google.com>
Subject: [PATCH v4 17/19] selftests/liveupdate: Test the targets of retrieved tmpfs files
Date: Wed, 11 Nov 2026 00:23:55 +0000 [thread overview]
Message-ID: <20261111002358.1842967-18-dmatlack@google.com> (raw)
In-Reply-To: <20261111002358.1842967-1-dmatlack@google.com>
Add test cases to luo_tmpfs for the file that a tmpfs file is retrieved
into. It must be opened for writing, but can be write-only, and it keeps
its file status flags. It must be an empty regular file on a tmpfs
mount, so files with data, a size or blocks past the end of the file are
rejected, and so are memfds, pipes, directories and immutable files. All
of these failures can be retried with another file.
Also check that an unprivileged process can retrieve into a file it
created, which keeps its owner and mode, that a running binary can be
preserved through a read-only fd, and that the retrieved binary can't be
executed until the session is finished, since LUO keeps it open for
writing until then. The exec test case runs a copy of the test binary.
Signed-off-by: David Matlack <dmatlack@google.com>
---
.../testing/selftests/liveupdate/luo_tmpfs.c | 490 +++++++++++++++++-
1 file changed, 488 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/liveupdate/luo_tmpfs.c b/tools/testing/selftests/liveupdate/luo_tmpfs.c
index 91a607752ee2..2d6dd8f0e138 100644
--- a/tools/testing/selftests/liveupdate/luo_tmpfs.c
+++ b/tools/testing/selftests/liveupdate/luo_tmpfs.c
@@ -69,8 +69,13 @@
#define SMALL_SIZE 8192
#define SPARSE_PAGES 8
+#define NOBODY 65534
#define TEST_UMASK 022
+/* The exec case runs a copy of this test with EXEC_ARG, see exec_main(). */
+#define EXEC_ARG "--exec"
+#define EXEC_STATUS 200
+
/* TAINT_BAD_PAGE, TAINT_DIE and TAINT_WARN. */
#define BAD_TAINT ((1L << 5) | (1L << 7) | (1L << 9))
@@ -129,8 +134,12 @@ struct test_case {
int (*preserve)(struct test_case *tc);
/* Stage 2: Retrieve the files and check them. */
int (*verify)(struct test_case *tc);
+ /* Stage 2: Optional checks after the session is finished. */
+ int (*after_finish)(struct test_case *tc);
/* The tokens of the case are token to token + 0xff. */
__u64 token;
+ /* State kept from verify() for after_finish(). */
+ int result;
};
static unsigned long page_size;
@@ -143,6 +152,11 @@ static int do_fcntl(int fd, int cmd, long arg)
return syscall(__NR_fcntl, fd, cmd, arg);
}
+static int do_fallocate(int fd, int mode, off_t offset, off_t len)
+{
+ return syscall(__NR_fallocate, fd, mode, offset, len);
+}
+
static int do_mount(const char *source, const char *target, const char *type,
unsigned long flags, const char *data)
{
@@ -437,6 +451,200 @@ static int preserve_memfd(struct test_case *tc, unsigned int flags,
return ret;
}
+static int drop_privileges(void)
+{
+ CHECK_EQ(syscall(__NR_setgroups, 0, NULL), 0);
+ CHECK_EQ(syscall(__NR_setresgid, NOBODY, NOBODY, NOBODY), 0);
+ CHECK_EQ(syscall(__NR_setresuid, NOBODY, NOBODY, NOBODY), 0);
+ return KSFT_PASS;
+}
+
+/* Run @fn(@tc) in a child process, and return its result. */
+static int run_child(int (*fn)(struct test_case *tc), struct test_case *tc)
+{
+ int status, ret;
+ pid_t pid;
+
+ fflush(stdout);
+ pid = fork();
+ CHECK(pid >= 0);
+ if (!pid) {
+ ret = fn(tc);
+ fflush(stdout);
+ _exit(ret);
+ }
+
+ CHECK_EQ(waitpid(pid, &status, 0), pid);
+ CHECK(WIFEXITED(status));
+ return WEXITSTATUS(status);
+}
+
+/*
+ * A copy of this test that is executed with EXEC_ARG writes a 0 byte to stdout
+ * once it runs, and exits with EXEC_STATUS once stdin is closed.
+ */
+static int exec_main(void)
+{
+ char c;
+
+ if (write(STDOUT_FILENO, "", 1) != 1)
+ return 1;
+
+ while (read(STDIN_FILENO, &c, 1) > 0)
+ continue;
+
+ return EXEC_STATUS;
+}
+
+/* Copy the binary of this test to a new file at @path. */
+static int copy_self(const char *path)
+{
+ static char buf[65536];
+ int in_fd, out_fd;
+ ssize_t len;
+
+ in_fd = open("/proc/self/exe", O_RDONLY);
+ CHECK(in_fd >= 0);
+ out_fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0755);
+ CHECK(out_fd >= 0);
+
+ while ((len = read(in_fd, buf, sizeof(buf))) > 0)
+ CHECK_EQ(write(out_fd, buf, len), len);
+ CHECK_EQ(len, 0);
+
+ close(out_fd);
+ close(in_fd);
+ return KSFT_PASS;
+}
+
+/* Return the number of pages of the binary of this test. */
+static long self_pages(void)
+{
+ struct stat st;
+
+ if (stat("/proc/self/exe", &st))
+ return -1;
+
+ return nr_pages(st.st_size);
+}
+
+/* Check that @fd has the contents of the binary of this test. */
+static int check_self(int fd)
+{
+ long long size = file_size(fd);
+ void *p, *self;
+ int self_fd;
+
+ self_fd = open("/proc/self/exe", O_RDONLY);
+ CHECK(self_fd >= 0);
+ CHECK(size > 0);
+ CHECK_EQ(file_size(self_fd), size);
+
+ p = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
+ CHECK(p != MAP_FAILED);
+ self = mmap(NULL, size, PROT_READ, MAP_SHARED, self_fd, 0);
+ CHECK(self != MAP_FAILED);
+ CHECK(!memcmp(p, self, size));
+
+ munmap(self, size);
+ munmap(p, size);
+ close(self_fd);
+ return KSFT_PASS;
+}
+
+/*
+ * Execute the copy of this test at @path and wait until it runs. Return the
+ * write end of its stdin, which ends it when closed, or -errno if it can't be
+ * executed.
+ */
+static int start_exe(const char *path, pid_t *pid)
+{
+ char *const argv[] = { (char *)path, EXEC_ARG, NULL };
+ char *const envp[] = { NULL };
+ unsigned char err = EIO;
+ int in[2], out[2], status;
+
+ if (pipe2(in, O_CLOEXEC) || pipe2(out, O_CLOEXEC))
+ return -errno;
+
+ fflush(stdout);
+ *pid = fork();
+ if (!*pid) {
+ if (dup2(in[0], STDIN_FILENO) >= 0 &&
+ dup2(out[1], STDOUT_FILENO) >= 0)
+ execve(path, argv, envp);
+
+ /* Report the error instead of the 0 byte from exec_main(). */
+ err = errno;
+ if (write(out[1], &err, 1) != 1)
+ _exit(2);
+ _exit(1);
+ }
+
+ close(in[0]);
+ close(out[1]);
+ if (*pid > 0 && read(out[0], &err, 1) != 1)
+ err = EIO;
+ close(out[0]);
+ if (!err)
+ return in[1];
+
+ close(in[1]);
+ if (*pid > 0)
+ waitpid(*pid, &status, 0);
+ return -err;
+}
+
+/* End a program started by start_exe() and check its exit status. */
+static int stop_exe(pid_t pid, int stdin_fd)
+{
+ int status;
+
+ close(stdin_fd);
+ CHECK_EQ(waitpid(pid, &status, 0), pid);
+ CHECK(WIFEXITED(status));
+ CHECK_EQ(WEXITSTATUS(status), EXEC_STATUS);
+ return KSFT_PASS;
+}
+
+/* Execute the copy of this test at @path. Return 0 or -errno. */
+static int run_exe(const char *path)
+{
+ pid_t pid = -1;
+ int stdin_fd;
+
+ stdin_fd = start_exe(path, &pid);
+ if (stdin_fd < 0)
+ return stdin_fd;
+
+ return stop_exe(pid, stdin_fd) == KSFT_PASS ? 0 : -ECHILD;
+}
+
+/*
+ * Preserve a copy of the binary of this test while it runs, through a
+ * read-only fd, since it can't be opened for writing.
+ */
+static int preserve_running_exe(struct test_case *tc)
+{
+ int fd, stdin_fd;
+ char path[128];
+ pid_t pid;
+
+ snprintf(path, sizeof(path), "src/%s", tc->name);
+ TRY(copy_self(path));
+
+ stdin_fd = start_exe(path, &pid);
+ CHECK(stdin_fd >= 0);
+ CHECK_ERRNO(open(path, O_RDWR), ETXTBSY);
+
+ fd = open(path, O_RDONLY);
+ CHECK(fd >= 0);
+ CHECK_EQ(luo_session_preserve_fd(session_fd, fd, tc->token), 0);
+ close(fd);
+
+ return stop_exe(pid, stdin_fd);
+}
+
/* Retrieve the file of @tc into a new file on a new mount at dst. */
static int restore_verify(struct test_case *tc)
{
@@ -600,6 +808,229 @@ static int linked_target_verify(struct test_case *tc)
return KSFT_PASS;
}
+/* Set the inode flag @flag, e.g. FS_IMMUTABLE_FL, on the file at @path. */
+static int set_inode_flag(const char *path, int flag)
+{
+ int fd, flags, ret = 0;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -errno;
+
+ if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) {
+ ret = -errno;
+ } else {
+ flags |= flag;
+ if (ioctl(fd, FS_IOC_SETFLAGS, &flags))
+ ret = -errno;
+ }
+
+ close(fd);
+ return ret;
+}
+
+static int target_rdonly_verify(struct test_case *tc)
+{
+ int target, rd_fd;
+
+ CHECK_EQ(mount_tmpfs("dst", 16, NULL), 0);
+ target = new_target("dst");
+ CHECK(target >= 0);
+ rd_fd = reopen_fd(target, O_RDONLY);
+ CHECK(rd_fd >= 0);
+
+ /* The target must be opened for writing. */
+ CHECK_EQ(retrieve_into(tc->token, rd_fd), -EBADF);
+ CHECK_EQ(file_size(target), 0);
+
+ /* The failure didn't consume the preserved file. */
+ CHECK_EQ(retrieve_into(tc->token, target), target);
+ TRY(check_restored(tc, target));
+
+ close(rd_fd);
+ close(target);
+ return KSFT_PASS;
+}
+
+static int target_wronly_verify(struct test_case *tc)
+{
+ int target, rd_fd;
+
+ CHECK_EQ(mount_tmpfs("dst", 16, NULL), 0);
+ target = open("dst", O_TMPFILE | O_WRONLY | O_APPEND, 0644);
+ CHECK(target >= 0);
+ CHECK_EQ(retrieve_into(tc->token, target), target);
+
+ /* The target keeps its flags. */
+ CHECK_EQ(do_fcntl(target, F_GETFL, 0) & (O_ACCMODE | O_APPEND),
+ O_WRONLY | O_APPEND);
+ CHECK_EQ(file_size(target), tc->size);
+
+ rd_fd = reopen_fd(target, O_RDONLY);
+ CHECK(rd_fd >= 0);
+ TRY(check_contents(rd_fd, tc->size, tc->token));
+
+ close(rd_fd);
+ close(target);
+ return KSFT_PASS;
+}
+
+static int target_not_empty_verify(struct test_case *tc)
+{
+ int target, fd;
+
+ /* The target must be empty: It can't have data... */
+ CHECK_EQ(mount_tmpfs("dst", 16, NULL), 0);
+ target = new_target("dst");
+ CHECK(target >= 0);
+ CHECK_EQ(write(target, "x", 1), 1);
+ CHECK_EQ(retrieve_into(tc->token, target), -EINVAL);
+ close(target);
+
+ /* ... a size... */
+ target = new_target("dst");
+ CHECK(target >= 0);
+ CHECK_EQ(ftruncate(target, page_size), 0);
+ CHECK_EQ(retrieve_into(tc->token, target), -EINVAL);
+ close(target);
+
+ /* ... or blocks past the end of the file. */
+ target = new_target("dst");
+ CHECK(target >= 0);
+ CHECK_EQ(do_fallocate(target, FALLOC_FL_KEEP_SIZE, 0, page_size), 0);
+ CHECK_EQ(file_size(target), 0);
+ CHECK_EQ(retrieve_into(tc->token, target), -EINVAL);
+ close(target);
+
+ /* The failures didn't consume the preserved file. */
+ fd = retrieve_new(tc->token, "dst");
+ CHECK(fd >= 0);
+ TRY(check_restored(tc, fd));
+
+ close(fd);
+ return KSFT_PASS;
+}
+
+static int target_not_tmpfs_verify(struct test_case *tc)
+{
+ int target, pipe_fds[2], fd;
+
+ /* Like the preserved file, the target must be on a tmpfs mount. */
+ target = memfd_create(tc->name, 0);
+ CHECK(target >= 0);
+ CHECK_EQ(retrieve_into(tc->token, target), -EINVAL);
+ close(target);
+
+ CHECK_EQ(pipe2(pipe_fds, O_CLOEXEC), 0);
+ CHECK_EQ(retrieve_into(tc->token, pipe_fds[1]), -EINVAL);
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+
+ /* It must also be a regular file. */
+ CHECK_EQ(mount_tmpfs("dst", 16, NULL), 0);
+ target = open("dst", O_RDONLY | O_DIRECTORY);
+ CHECK(target >= 0);
+ CHECK_EQ(retrieve_into(tc->token, target), -EINVAL);
+ close(target);
+
+ /* The failures didn't consume the preserved file. */
+ fd = retrieve_new(tc->token, "dst");
+ CHECK(fd >= 0);
+ TRY(check_restored(tc, fd));
+
+ close(fd);
+ return KSFT_PASS;
+}
+
+static int target_immutable_verify(struct test_case *tc)
+{
+ int target, fd, ret;
+
+ CHECK_EQ(mount_tmpfs("dst", 16, NULL), 0);
+ target = open("dst/f", O_CREAT | O_EXCL | O_RDWR, 0644);
+ CHECK(target >= 0);
+ ret = set_inode_flag("dst/f", FS_IMMUTABLE_FL);
+ if (ret == -ENOTTY || ret == -EOPNOTSUPP) {
+ ksft_print_msg("%s: FS_IOC_SETFLAGS is not supported\n",
+ tc->name);
+ close(target);
+ return KSFT_SKIP;
+ }
+ CHECK_EQ(ret, 0);
+
+ /* An immutable file can't be the target, even if opened before. */
+ CHECK_EQ(retrieve_into(tc->token, target), -EPERM);
+ CHECK_EQ(file_size(target), 0);
+ close(target);
+
+ /* The failure didn't consume the preserved file. */
+ fd = retrieve_new(tc->token, "dst");
+ CHECK(fd >= 0);
+ TRY(check_restored(tc, fd));
+
+ close(fd);
+ return KSFT_PASS;
+}
+
+static int unprivileged_child(struct test_case *tc)
+{
+ struct stat st;
+ int target;
+
+ TRY(drop_privileges());
+
+ /* The file keeps the owner and mode it was created with. */
+ target = open("dst/nobody", O_TMPFILE | O_RDWR, 0600);
+ CHECK(target >= 0);
+ CHECK_EQ(retrieve_into(tc->token, target), target);
+ TRY(check_restored(tc, target));
+ CHECK(!fstat(target, &st));
+ CHECK_EQ(st.st_uid, NOBODY);
+ CHECK_EQ(st.st_gid, NOBODY);
+ CHECK_EQ(st.st_mode & 07777, 0600);
+
+ close(target);
+ return KSFT_PASS;
+}
+
+static int unprivileged_verify(struct test_case *tc)
+{
+ CHECK_EQ(mount_tmpfs("dst", 16, "mode=0755"), 0);
+ CHECK_EQ(mkdir("dst/nobody", 0755), 0);
+ CHECK_EQ(chown("dst/nobody", NOBODY, NOBODY), 0);
+
+ return run_child(unprivileged_child, tc);
+}
+
+static int exec_verify(struct test_case *tc)
+{
+ long pages = self_pages();
+ int target;
+
+ CHECK(pages > 0);
+ CHECK_EQ(mount_tmpfs("dst", pages, NULL), 0);
+ target = open("dst", O_TMPFILE | O_RDWR, 0755);
+ CHECK(target >= 0);
+ CHECK_EQ(retrieve_into(tc->token, target), target);
+ TRY(check_self(target));
+ CHECK_EQ(mnt_used("dst"), pages);
+ CHECK_EQ(link_fd(target, "dst/exe"), 0);
+ close(target);
+
+ /*
+ * LUO keeps the file open for writing until the session is finished,
+ * so the binary can't be executed before that.
+ */
+ CHECK_EQ(run_exe("dst/exe"), -ETXTBSY);
+ return KSFT_PASS;
+}
+
+static int exec_after_finish(struct test_case *tc)
+{
+ CHECK_EQ(run_exe("dst/exe"), 0);
+ return KSFT_PASS;
+}
+
static int memfd_preserve(struct test_case *tc)
{
return preserve_memfd(tc, 0, 0);
@@ -856,6 +1287,43 @@ static struct test_case test_cases[] = {
.size = SMALL_SIZE,
.verify = linked_target_verify,
},
+ /* The target file. */
+ {
+ .name = "target_rdonly",
+ .size = SMALL_SIZE,
+ .verify = target_rdonly_verify,
+ },
+ {
+ .name = "target_wronly",
+ .size = SMALL_SIZE,
+ .verify = target_wronly_verify,
+ },
+ {
+ .name = "target_not_empty",
+ .size = SMALL_SIZE,
+ .verify = target_not_empty_verify,
+ },
+ {
+ .name = "target_not_tmpfs",
+ .size = SMALL_SIZE,
+ .verify = target_not_tmpfs_verify,
+ },
+ {
+ .name = "target_immutable",
+ .size = SMALL_SIZE,
+ .verify = target_immutable_verify,
+ },
+ {
+ .name = "unprivileged",
+ .size = SMALL_SIZE,
+ .verify = unprivileged_verify,
+ },
+ {
+ .name = "exec",
+ .preserve = preserve_running_exe,
+ .verify = exec_verify,
+ .after_finish = exec_after_finish,
+ },
/* memfds. */
{
.name = "memfd",
@@ -987,7 +1455,7 @@ static void run_stage_2(int luo_fd, int state_session_fd)
long taint = read_taint();
unsigned int i, nr_tests;
struct test_case *tc;
- int stage;
+ int stage, ret;
ksft_print_msg("[STAGE 2] Starting post-kexec verification...\n");
restore_and_read_stage(state_session_fd, STATE_TOKEN, &stage);
@@ -1001,6 +1469,8 @@ static void run_stage_2(int luo_fd, int state_session_fd)
fail_exit("luo_retrieve_session for '%s'", SESSION_NAME);
nr_tests = ARRAY_SIZE(test_cases) + 1;
+ for (i = 0; i < ARRAY_SIZE(test_cases); i++)
+ nr_tests += !!test_cases[i].after_finish;
ksft_print_header();
ksft_set_plan(nr_tests);
@@ -1008,13 +1478,26 @@ static void run_stage_2(int luo_fd, int state_session_fd)
for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
tc = &test_cases[i];
enter_case_dir(tc);
- ksft_test_result_report(tc->verify(tc), "%s\n", tc->name);
+ tc->result = tc->verify(tc);
+ ksft_test_result_report(tc->result, "%s\n", tc->name);
}
if (luo_session_finish(session_fd))
fail_exit("luo_session_finish for '%s'", SESSION_NAME);
close(session_fd);
+ for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
+ tc = &test_cases[i];
+ if (!tc->after_finish)
+ continue;
+
+ enter_case_dir(tc);
+ ret = KSFT_SKIP;
+ if (tc->result == KSFT_PASS)
+ ret = tc->after_finish(tc);
+ ksft_test_result_report(ret, "%s_after_finish\n", tc->name);
+ }
+
if (chdir("/") || syscall(__NR_umount2, TEST_DIR, MNT_DETACH) ||
rmdir(TEST_DIR))
fail_exit("Failed to clean up %s", TEST_DIR);
@@ -1033,6 +1516,9 @@ static void run_stage_2(int luo_fd, int state_session_fd)
int main(int argc, char *argv[])
{
+ if (argc == 2 && !strcmp(argv[1], EXEC_ARG))
+ return exec_main();
+
return luo_test(argc, argv, STATE_SESSION_NAME,
run_stage_1, run_stage_2);
}
--
2.56.0.542.g3ef89b7020-goog
next prev parent reply other threads:[~2026-11-11 0:24 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-11 0:23 [PATCH v4 00/19] liveupdate: Preserve tmpfs files across Live Update David Matlack
2026-11-11 0:23 ` [PATCH v4 01/19] liveupdate: Remove the list of steps from luo_preserve_file()'s comment David Matlack
2026-11-11 0:23 ` [PATCH v4 02/19] liveupdate: Pass user argument to file preserve and retrieve David Matlack
2026-11-11 0:23 ` [PATCH v4 03/19] selftests/liveupdate: Add tests for preserve and retrieve user argument David Matlack
2026-11-11 0:23 ` [PATCH v4 04/19] liveupdate: Allow file handlers to mark retrieve failures as retryable David Matlack
2026-11-11 0:23 ` [PATCH v4 05/19] mm: Rename memfd_luo to shmem_luo David Matlack
2026-11-11 0:23 ` [PATCH v4 06/19] liveupdate: Don't WARN if tracking a retrieved file fails David Matlack
2026-11-11 0:23 ` [PATCH v4 07/19] liveupdate: Add helpers to track the files that LUO manages David Matlack
2026-11-11 0:23 ` [PATCH v4 08/19] liveupdate: Let file handlers retrieve into an existing file David Matlack
2026-11-11 0:23 ` [PATCH v4 09/19] mm/shmem_luo: Leave freeing the preserved state to finish() David Matlack
2026-11-11 0:23 ` [PATCH v4 10/19] mm/shmem_luo: Don't make early retrieve failures permanent David Matlack
2026-11-11 0:23 ` [PATCH v4 11/19] mm/shmem_luo: Require permission to preserve a file David Matlack
2026-11-11 0:23 ` [PATCH v4 12/19] mm/shmem_luo: Retrieve tmpfs files into a file created by userspace David Matlack
2026-11-11 0:23 ` [PATCH v4 13/19] mm/shmem_luo: Allow preserving tmpfs files David Matlack
2026-11-11 0:23 ` [PATCH v4 14/19] selftests/liveupdate: Test which tmpfs files can be preserved David Matlack
2026-11-11 0:23 ` [PATCH v4 15/19] selftests/liveupdate: Add a test for retrieving tmpfs files David Matlack
2026-11-11 0:23 ` [PATCH v4 16/19] selftests/liveupdate: Test memfds and LUO interactions in luo_tmpfs David Matlack
2026-11-11 0:23 ` David Matlack [this message]
2026-11-11 0:23 ` [PATCH v4 18/19] selftests/liveupdate: Test retrieving tmpfs files preserved read-only David Matlack
2026-11-11 0:23 ` [PATCH v4 19/19] selftests/liveupdate: Test block accounting of retrieved tmpfs files David Matlack
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261111002358.1842967-18-dmatlack@google.com \
--to=dmatlack@google.com \
--cc=akpm@linux-foundation.org \
--cc=baolin.wang@linux.alibaba.com \
--cc=brauner@kernel.org \
--cc=corbet@lwn.net \
--cc=david@kernel.org \
--cc=graf@amazon.com \
--cc=hughd@google.com \
--cc=kexec@lists.infradead.org \
--cc=liam@infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=mhocko@suse.com \
--cc=pasha.tatashin@soleen.com \
--cc=pratyush@kernel.org \
--cc=rdunlap@infradead.org \
--cc=rppt@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=skhawaja@google.com \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®