Use private mount snapshots and preserve unknown mount state

This commit is contained in:
Roman Tarasov committed 2026-09-21 16:59:46 +03:00
1 parent 4102fa7aa5
commit a67ff8fc4a
6 files changed
+125 -45

No files matched your search

+6
View File
@@ -8,6 +8,12 @@
#include "sm_limits.h"
struct statfs;
// Return a private mount-table snapshot; the caller frees *mounts_out.
// Returns the entry count, or -1 with *mounts_out == NULL on failure.
int sm_mount_table_snapshot(struct statfs **mounts_out);
// Check whether a title is present in the installed app set.
bool is_installed(const char *title_id);
// Check whether /user/appmeta/<TITLE_ID>/param.json exists.
+12 -5
View File
@@ -701,8 +701,8 @@ static struct json_object *filesystem_to_json(const struct statfs *mount) {
static void handle_storage(struct MHD_Connection *connection) {
struct statfs *mounts = NULL;
int mount_count = getmntinfo(&mounts, MNT_NOWAIT);
if (mount_count < 0 || !mounts) {
int mount_count = sm_mount_table_snapshot(&mounts);
if (mount_count < 0) {
int status = errno != 0 ? errno : EIO;
send_error_response(connection, operation_http_status(status), status,
strerror(status));
@@ -712,6 +712,7 @@ static void handle_storage(struct MHD_Connection *connection) {
struct json_object *response = new_status_response(0);
struct json_object *items = json_object_new_array_ext(mount_count);
if (!response || !items) {
free(mounts);
if (response)
json_object_put(response);
if (items)
@@ -726,6 +727,7 @@ static void handle_storage(struct MHD_Connection *connection) {
continue;
struct json_object *item = filesystem_to_json(&mounts[i]);
if (!item || json_object_array_add(items, item) != 0) {
free(mounts);
if (item)
json_object_put(item);
json_object_put(items);
@@ -735,9 +737,15 @@ static void handle_storage(struct MHD_Connection *connection) {
}
count++;
}
free(mounts);
if (!add_json_int(response, "count", (int64_t)count) ||
!add_json_value(response, "mounts", items)) {
if (!add_json_int(response, "count", (int64_t)count)) {
json_object_put(items);
json_object_put(response);
send_out_of_memory_response(connection);
return;
}
if (!add_json_value(response, "mounts", items)) {
json_object_put(response);
send_out_of_memory_response(connection);
return;
@@ -2555,7 +2563,6 @@ static void *storage_job_thread_main(void *arg) {
if (unpack_mounted) {
if (!sm_shellcore_release_title_runtime(title_id) && status == 0)
status = errno == EBUSY ? EBUSY : EIO;
unpack_mounted = false;
}
if (status == 0 && operation == GAME_STORAGE_UNPACK && delete_source &&
sm_storage_delete_path(source) != 0) {
+6 -3
View File
@@ -565,17 +565,20 @@ static bool cache_root_is_mounted(const char *cache_root) {
return false;
struct statfs *mounts = NULL;
int mount_count = getmntinfo(&mounts, MNT_NOWAIT);
if (mount_count <= 0 || !mounts)
int mount_count = sm_mount_table_snapshot(&mounts);
if (mount_count < 0)
return true;
for (int i = 0; i < mount_count; ++i) {
const char *source = mounts[i].f_mntfromname;
if (strncmp(source, "<above>:", 8) == 0)
source += 8;
if (strcmp(source, cache_fakelib) == 0)
if (strcmp(source, cache_fakelib) == 0) {
free(mounts);
return true;
}
}
free(mounts);
return false;
}
+69 -29
View File
@@ -14,6 +14,41 @@ static bool unmount_controlled_mount_stack(const char *path);
static bool unmount_controlled_mount_stack_impl(const char *path,
bool diagnose_busy);
int sm_mount_table_snapshot(struct statfs **mounts_out) {
*mounts_out = NULL;
int count = getfsstat(NULL, 0, MNT_NOWAIT);
if (count < 0)
return -1;
// A full buffer may be truncated by concurrent mounts. Grow it a bounded
// number of times before reporting an unavailable table.
size_t capacity = (size_t)count + 16u;
for (int attempt = 0; attempt < 3; ++attempt) {
if (capacity > (size_t)LONG_MAX / sizeof(struct statfs)) {
errno = EOVERFLOW;
return -1;
}
size_t bytes = capacity * sizeof(struct statfs);
struct statfs *mounts = malloc(bytes);
if (!mounts)
return -1;
count = getfsstat(mounts, (long)bytes, MNT_NOWAIT);
if (count >= 0 && (size_t)count < capacity) {
*mounts_out = mounts;
return count;
}
int saved_errno = errno;
free(mounts);
if (count < 0) {
errno = saved_errno;
return -1;
}
capacity *= 2u;
}
errno = EAGAIN;
return -1;
}
// --- FILESYSTEM ---
bool is_installed(const char *title_id) {
char path[MAX_PATH];
@@ -320,9 +355,6 @@ static bool source_path_needs_cleanup(const char *source_path) {
snprintf(eboot_path, sizeof(eboot_path), "%s/eboot.bin", source_path);
if (written < 0 || (size_t)written >= sizeof(eboot_path))
return true;
if (path_exists(eboot_path))
return false;
return !path_exists(eboot_path);
}
@@ -410,16 +442,17 @@ void cleanup_staged_mount_links(void) {
cleanup_staged_mount_links_entry, NULL);
}
static bool get_top_mount(const char *path, struct statfs *mount_st_out) {
// 1: mounted, 0: absent, -1: mount table unavailable.
static int get_top_mount(const char *path, struct statfs *mount_st_out) {
if (statfs(path, mount_st_out) == 0 &&
strcmp(mount_st_out->f_mntonname, path) == 0) {
return true;
return 1;
}
struct statfs *mntbuf = NULL;
int mntcount = getmntinfo(&mntbuf, MNT_NOWAIT);
if (mntcount <= 0 || !mntbuf)
return false;
int mntcount = sm_mount_table_snapshot(&mntbuf);
if (mntcount < 0)
return -1;
const struct statfs *best_mount = NULL;
for (int i = 0; i < mntcount; i++) {
@@ -432,11 +465,11 @@ static bool get_top_mount(const char *path, struct statfs *mount_st_out) {
}
}
if (!best_mount)
return false;
*mount_st_out = *best_mount;
return true;
bool found = best_mount != NULL;
if (found)
*mount_st_out = *best_mount;
free(mntbuf);
return found;
}
typedef enum {
@@ -502,7 +535,9 @@ static bool inspect_title_stack(const char *title_id, const char *source_path,
}
struct statfs *mntbuf = NULL;
int mntcount = getmntinfo(&mntbuf, MNT_NOWAIT);
int mntcount = sm_mount_table_snapshot(&mntbuf);
if (mntcount < 0)
return false;
const struct statfs *best_mount = NULL;
bool found_mount_entry = false;
if (mntcount > 0 && mntbuf) {
@@ -558,19 +593,15 @@ static bool inspect_title_stack(const char *title_id, const char *source_path,
} else if (!found_mount_entry) {
log_debug(" [LINK] no mount entries for %s", state_out->system_ex_path);
}
for (int i = 0; i < mntcount; i++) {
if (strcmp(mntbuf[i].f_mntonname, state_out->system_ex_path) != 0)
continue;
log_debug(" [LINK] mount entry for %s: type=%s from=%s flags=0x%lX",
state_out->system_ex_path, mntbuf[i].f_fstypename,
mntbuf[i].f_mntfromname, (unsigned long)mntbuf[i].f_flags);
}
free(mntbuf);
errno = inspect_errno;
return false;
}
if (!top_mount)
if (!top_mount) {
free(mntbuf);
return true;
}
const char *top_from = top_mount->f_mntfromname;
if (strcmp(top_mount->f_fstypename, "unionfs") == 0 &&
@@ -594,14 +625,16 @@ static bool inspect_title_stack(const char *title_id, const char *source_path,
state_out->has_our_backport = true;
}
free(mntbuf);
return true;
}
static bool unmount_top_controlled_layer_impl(const char *path,
bool diagnose_busy) {
struct statfs mount_st;
if (!get_top_mount(path, &mount_st))
return true;
int mounted = get_top_mount(path, &mount_st);
if (mounted <= 0)
return mounted == 0;
bool controlled_top =
(strcmp(mount_st.f_fstypename, "unionfs") == 0) ||
@@ -802,8 +835,8 @@ static void log_backport_overlay_failure(const char *mount_point,
log_backport_overlay_path("covered", mount_point, NULL, mounted_sfs);
struct statfs *mounts = NULL;
int mount_count = getmntinfo(&mounts, MNT_NOWAIT);
if (mount_count <= 0 || !mounts) {
int mount_count = sm_mount_table_snapshot(&mounts);
if (mount_count < 0) {
int table_errno = errno;
log_debug(" [IMG][UNIONFS] mount table unavailable: error=%d (%s)",
table_errno, strerror(table_errno));
@@ -823,6 +856,7 @@ static void log_backport_overlay_failure(const char *mount_point,
log_debug(" [IMG][UNIONFS] no exact mount-table entry for %s",
mount_point);
}
free(mounts);
}
bool mount_backport_overlay(const char *mount_point,
@@ -872,14 +906,15 @@ static bool unmount_controlled_mount_stack_impl(const char *path,
bool diagnose_busy) {
for (int i = 0; i < MAX_LAYERED_UNMOUNT_ATTEMPTS * 4; i++) {
struct statfs mount_st;
if (!get_top_mount(path, &mount_st))
return true;
int mounted = get_top_mount(path, &mount_st);
if (mounted <= 0)
return mounted == 0;
if (!unmount_top_controlled_layer_impl(path, diagnose_busy))
return false;
}
struct statfs mount_st;
return !get_top_mount(path, &mount_st);
return get_top_mount(path, &mount_st) == 0;
}
static bool unmount_controlled_mount_stack(const char *path) {
@@ -1422,6 +1457,11 @@ static bool cleanup_mount_links_entry(const char *title_id,
memset(&state, 0, sizeof(state));
bool inspected = inspect_title_stack(title_id, source_path,
ctx->removed_source_root, &state);
if (!inspected) {
log_debug(" [LINK] keeping mount link for %s: mount state unavailable",
title_id);
return true;
}
bool top_is_ours = inspected && title_stack_top_is_managed(&state);
bool stack_matches_link = false;
if (inspected && source_path[0] != '\0') {
+28 -6
View File
@@ -1,6 +1,7 @@
#include "sm_platform.h"
#include "sm_runtime.h"
#include "sm_mount_device.h"
#include "sm_filesystem.h"
#include "sm_image_cache.h"
#include "sm_limits.h"
#include "sm_log.h"
@@ -87,8 +88,8 @@ bool resolve_device_from_mount(const char *mount_point,
}
struct statfs *mntbuf = NULL;
int mntcount = getmntinfo(&mntbuf, MNT_NOWAIT);
if (mntcount <= 0 || !mntbuf)
int mntcount = sm_mount_table_snapshot(&mntbuf);
if (mntcount < 0)
return false;
for (int i = 0; i < mntcount; i++) {
@@ -96,14 +97,17 @@ bool resolve_device_from_mount(const char *mount_point,
continue;
if (parse_unit_from_dev_path(mntbuf[i].f_mntfromname, "/dev/lvd", unit_out)) {
*backend_out = ATTACH_BACKEND_LVD;
free(mntbuf);
return true;
}
if (parse_unit_from_dev_path(mntbuf[i].f_mntfromname, "/dev/md", unit_out)) {
*backend_out = ATTACH_BACKEND_MD;
free(mntbuf);
return true;
}
}
free(mntbuf);
return false;
}
@@ -115,11 +119,16 @@ static bool is_path_mountpoint(const char *path) {
// An unreadable filesystem can make statfs(path) fail while it is still in
// the mount table. Treat it as active until unmount removes that entry.
struct statfs *mntbuf = NULL;
int mntcount = getmntinfo(&mntbuf, MNT_NOWAIT);
int mntcount = sm_mount_table_snapshot(&mntbuf);
if (mntcount < 0)
return true; // Do not detach a device when its mount state is unknown.
for (int i = 0; i < mntcount && mntbuf; ++i) {
if (strcmp(mntbuf[i].f_mntonname, path) == 0)
if (strcmp(mntbuf[i].f_mntonname, path) == 0) {
free(mntbuf);
return true;
}
}
free(mntbuf);
return false;
}
@@ -130,7 +139,11 @@ bool is_active_image_mount_point(const char *path) {
bool wait_for_lvd_release(void) {
for (unsigned int waited_us = 0;; waited_us += LVD_RELEASE_WAIT_POLL_US) {
struct statfs *mntbuf = NULL;
int mntcount = getmntinfo(&mntbuf, MNT_NOWAIT);
int mntcount = sm_mount_table_snapshot(&mntbuf);
if (mntcount < 0) {
log_debug(" [IMG][LVD] mount table unavailable: %s", strerror(errno));
return false;
}
bool mounted = false;
for (int i = 0; i < mntcount && mntbuf; i++) {
if (strcmp(mntbuf[i].f_mntfromname, "/dev/lvd2") != 0)
@@ -139,6 +152,7 @@ bool wait_for_lvd_release(void) {
break;
}
if (!mounted) {
free(mntbuf);
if (waited_us != 0)
log_debug(" [IMG][LVD] /dev/lvd2 released");
return true;
@@ -164,6 +178,7 @@ bool wait_for_lvd_release(void) {
(unsigned long)mntbuf[i].f_flags);
}
}
free(mntbuf);
if (should_stop_requested())
return false;
if (waited_us >= LVD_RELEASE_WAIT_MAX_US) {
@@ -198,14 +213,21 @@ static bool read_device_node_identity(
static bool device_node_is_mounted(const char *devname,
char mount_point[MNAMELEN]) {
struct statfs *mounts = NULL;
int mount_count = getmntinfo(&mounts, MNT_NOWAIT);
int mount_count = sm_mount_table_snapshot(&mounts);
if (mount_count < 0) {
if (mount_point)
(void)strlcpy(mount_point, "unknown", MNAMELEN);
return true;
}
for (int i = 0; i < mount_count && mounts; ++i) {
if (strcmp(mounts[i].f_mntfromname, devname) != 0)
continue;
if (mount_point)
(void)strlcpy(mount_point, mounts[i].f_mntonname, MNAMELEN);
free(mounts);
return true;
}
free(mounts);
return false;
}
+4 -2
View File
@@ -5,6 +5,7 @@
#include <sys/vnode.h>
#include "sm_log.h"
#include "sm_filesystem.h"
#include "sm_mount_diag.h"
#include "sm_path_utils.h"
@@ -117,8 +118,8 @@ static void collect_mount_dependencies(const char *mount_point,
(void)add_diag_root(roots, mount_point);
struct statfs *mounts = NULL;
int mount_count = getmntinfo(&mounts, MNT_NOWAIT);
if (mount_count <= 0 || !mounts) {
int mount_count = sm_mount_table_snapshot(&mounts);
if (mount_count < 0) {
log_debug(" [MOUNT-BUSY] mount table unavailable for %s: %s",
mount_point, strerror(errno));
return;
@@ -141,6 +142,7 @@ static void collect_mount_dependencies(const char *mount_point,
if (consumes_mount)
(void)add_diag_root(roots, target);
}
free(mounts);
*dependency_count_out = dependency_count;
}