mirror of
https://github.com/drakmor/ShadowMountPlus.git
synced 2026-10-22 14:42:27 -05:00
Use private mount snapshots and preserve unknown mount state
This commit is contained in:
1 parent
4102fa7aa5
commit
a67ff8fc4a
6 files changed
+125
-45
No files matched your search
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user