diff --git a/src/magic_mount.c b/src/magic_mount.c index 070a879..f81cad9 100644 --- a/src/magic_mount.c +++ b/src/magic_mount.c @@ -314,6 +314,170 @@ static int node_collect(Node *self, const char *dir, const char *module_name, return 0; } +static int handle_symlink_compatibility(Node *system, const char *part_name) +{ + if (!system || !part_name) + return -1; + + Node *sys_child = node_find_child(system, part_name); + if (!sys_child || sys_child->type != NFT_SYMLINK) + return 0; + + if (!sys_child->module_path) + return 0; + + char link_target[PATH_MAX]; + ssize_t len = readlink(sys_child->module_path, link_target, sizeof(link_target) - 1); + if (len < 0) { + LOGW("readlink %s failed: %s", sys_child->module_path, strerror(errno)); + return 0; + } + link_target[len] = '\0'; + + size_t target_len = strlen(link_target); + while (target_len > 0 && link_target[target_len - 1] == '/') { + link_target[--target_len] = '\0'; + } + + if (target_len == 0) { + LOGW("empty symlink target for %s", part_name); + return 0; + } + + // check ../ or ./ + char expected_relative[PATH_MAX]; + char expected_absolute[PATH_MAX]; + snprintf(expected_relative, sizeof(expected_relative), "../%s", part_name); + snprintf(expected_absolute, sizeof(expected_absolute), "/%s", part_name); + + bool is_relative = (strcmp(link_target, expected_relative) == 0); + bool is_absolute = (strcmp(link_target, expected_absolute) == 0); + + if (!is_relative && !is_absolute) { + LOGD("symlink %s -> %s (not pointing to sibling directory, expected %s or %s)", + part_name, link_target, expected_relative, expected_absolute); + return 0; + } + + LOGI("found compatible symlink: system/%s -> %s", part_name, link_target); + + DIR *mod_dir = opendir(g_module_dir); + if (!mod_dir) { + LOGE("opendir %s: %s", g_module_dir, strerror(errno)); + return -1; + } + + struct dirent *mod_de; + bool found_real_dir = false; + char real_part_path[PATH_MAX]; + char module_name_buf[256] = {0}; + + while ((mod_de = readdir(mod_dir))) { + if (!strcmp(mod_de->d_name, ".") || !strcmp(mod_de->d_name, "..")) + continue; + + char mod_path[PATH_MAX]; + char part_path[PATH_MAX]; + + if (path_join(g_module_dir, mod_de->d_name, mod_path, sizeof(mod_path)) != 0) + continue; + + struct stat mod_st; + if (stat(mod_path, &mod_st) < 0 || !S_ISDIR(mod_st.st_mode)) + continue; + + if (module_disabled(mod_path)) + continue; + + // check / + if (path_join(mod_path, part_name, part_path, sizeof(part_path)) != 0) + continue; + + if (path_is_dir(part_path)) { + found_real_dir = true; + strncpy(real_part_path, part_path, sizeof(real_part_path) - 1); + real_part_path[sizeof(real_part_path) - 1] = '\0'; + strncpy(module_name_buf, mod_de->d_name, sizeof(module_name_buf) - 1); + module_name_buf[sizeof(module_name_buf) - 1] = '\0'; + break; + } + } + closedir(mod_dir); + + if (!found_real_dir) { + LOGD("no real directory found for %s, keeping symlink", part_name); + return 0; + } + + LOGI("symlink compatibility: system/%s -> %s, real dir in module '%s'", + part_name, link_target, module_name_buf); + + Node *new_part = node_new(part_name, NFT_DIRECTORY); + if (!new_part) { + LOGE("failed to create node for %s", part_name); + return -1; + } + + bool part_has_any = false; + if (node_collect(new_part, real_part_path, module_name_buf, &part_has_any) != 0) { + LOGE("failed to collect %s from %s", part_name, real_part_path); + node_free(new_part); + return -1; + } + + if (!part_has_any) { + LOGD("no content in %s, keeping symlink", part_name); + node_free(new_part); + return 0; + } + + Node *removed = node_take_child(system, part_name); + if (removed) { + node_free(removed); + LOGD("removed symlink node: system/%s", part_name); + } + + new_part->module_name = strdup(module_name_buf); + if (node_add_child(system, new_part) != 0) { + LOGE("failed to add directory node for %s", part_name); + node_free(new_part); + return -1; + } + + LOGI("replaced symlink with directory node: %s (from module '%s')", + part_name, module_name_buf); + + return 0; +} + +static int handle_all_symlink_compatibility(Node *system) +{ + if (!system) + return -1; + + const char *builtin_parts[] = { + "vendor", + "system_ext", + "product", + "odm", + }; + + for (size_t i = 0; i < sizeof(builtin_parts) / sizeof(builtin_parts[0]); ++i) { + if (handle_symlink_compatibility(system, builtin_parts[i]) != 0) { + LOGE("failed to handle symlink compatibility for %s", builtin_parts[i]); + } + } + + for (int i = 0; i < g_extra_parts_count; ++i) { + if (handle_symlink_compatibility(system, g_extra_parts[i]) != 0) { + LOGE("failed to handle symlink compatibility for extra part %s", + g_extra_parts[i]); + } + } + + return 0; +} + static Node *collect_root(void) { const char *mdir = g_module_dir; @@ -391,6 +555,12 @@ static Node *collect_root(void) g_stats.nodes_total += 2; + // ============ 处理 Symlink 兼容性 ============ + if (handle_all_symlink_compatibility(system) != 0) { + LOGW("symlink compatibility handling encountered errors (continuing anyway)"); + } + // ============================================ + struct { const char *name; bool need_symlink; @@ -566,27 +736,39 @@ static int do_magic(const char *base, const char *wbase, Node *node, if (path_join(path, c->name, rp, sizeof(rp)) != 0) return -1; + LOGD("checking child: parent=%s, child=%s, joined_path=%s", + path, c->name, rp); + bool need = false; if (c->type == NFT_SYMLINK) { need = true; + LOGD("child %s is SYMLINK", c->name); } else if (c->type == NFT_WHITEOUT) { need = path_exists(rp); + LOGD("child %s is WHITEOUT, path_exists=%d, need=%d", c->name, need, need); } else { struct stat st; if (lstat(rp, &st) == 0) { NodeFileType rt = node_type_from_stat(&st); + LOGD("type mismatch check: %s - expected=%d, actual=%d, is_symlink=%d", + rp, c->type, rt, rt == NFT_SYMLINK ? 1 : 0); if (rt != c->type || rt == NFT_SYMLINK) need = true; } else { + LOGD("lstat failed for %s: %s (errno=%d), path_exists=%d", + rp, strerror(errno), errno, path_exists(rp) ? 1 : 0); need = true; } } + LOGD("child check: parent=%s, child=%s, type=%d, need=%d, has_module_path=%d", + path, c->name, c->type, need, node->module_path ? 1 : 0); + if (need) { if (!node->module_path) { - LOGE("cannot create tmpfs on %s (%s)", - path, c->name); + LOGE("cannot create tmpfs on %s (%s) - child type: %d, target exists: %d", + path, c->name, c->type, path_exists(rp) ? 1 : 0); c->skip = true; g_stats.nodes_skipped++; continue;