feat: add symlink compatibility for module partition layout
Support non-standard module structure where system/<partition> uses
symlinks (e.g., system/vendor -> ../vendor) pointing to sibling
directories at module root level.
Problem:
When a module uses the layout:
module/
├── system/
│ └── vendor -> ../vendor
└── vendor/
└── etc/config
The symlink node in system/ would conflict with the real vendor/
directory, causing mounts to be skipped or fail.
This commit is contained in:
parent
cc05473df7
commit
e3ffb43a1b
1 changed files with 184 additions and 2 deletions
|
|
@ -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 ../<part_name> or ./<part_name>
|
||||
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 <module>/<part_name>
|
||||
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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue