/** * Get the binding for the given @path (relatively to the given * binding @side). */ static const Binding *get_binding(Tracee *tracee, Side side, const char path[PATH_MAX]) { const Binding *binding; size_t path_length = strlen(path); /* Sanity checks. */ assert(path != NULL && path[0] == '/'); CIRCLEQ_FOREACH_(tracee, binding, side) { Comparison comparison; const Path *ref; switch (side) { case GUEST: ref = &binding->guest; break; case HOST: ref = &binding->host; break; default: assert(0); return NULL; } comparison = compare_paths2(ref->path, ref->length, path, path_length); if ( comparison != PATHS_ARE_EQUAL && comparison != PATH1_IS_PREFIX) continue; /* Avoid false positive when a prefix of the rootfs is * used as an asymmetric binding, ex.: * * proot -m /usr:/location /usr/local/slackware */ if ( side == HOST && compare_paths(get_root(tracee), "/") != PATHS_ARE_EQUAL && belongs_to_guestfs(tracee, path)) continue; return binding; }
/** * Remove/substitute the leading part of a "translated" @path. It * returns 0 if no transformation is required (ie. symmetric binding), * otherwise it returns the size in bytes of the updated @path, * including the end-of-string terminator. On error it returns * -errno. */ int detranslate_path(Tracee *tracee, char path[PATH_MAX], const char t_referrer[PATH_MAX]) { size_t prefix_length; ssize_t new_length; bool sanity_check; bool follow_binding; /* Sanity check. */ if (strnlen(path, PATH_MAX) >= PATH_MAX) return -ENAMETOOLONG; /* Don't try to detranslate relative paths (typically the * target of a relative symbolic link). */ if (path[0] != '/') return 0; /* Is it a symlink? */ if (t_referrer != NULL) { Comparison comparison; sanity_check = false; follow_binding = false; /* In some cases bindings have to be resolved. */ comparison = compare_paths("/proc", t_referrer); if (comparison == PATH1_IS_PREFIX) { /* Some links in "/proc" are generated * dynamically by the kernel. PRoot has to * emulate some of them. */ char proc_path[PATH_MAX]; strcpy(proc_path, path); new_length = readlink_proc2(tracee, proc_path, t_referrer); if (new_length < 0) return new_length; if (new_length != 0) { strcpy(path, proc_path); return new_length + 1; } /* Always resolve bindings for symlinks in * "/proc", they always point to the emulated * file-system namespace by design. */ follow_binding = true; } else if (!belongs_to_guestfs(tracee, t_referrer)) { const char *binding_referree; const char *binding_referrer; binding_referree = get_path_binding(tracee, HOST, path); binding_referrer = get_path_binding(tracee, HOST, t_referrer); assert(binding_referrer != NULL); /* Resolve bindings for symlinks that belong * to a binding and point to the same binding. * For example, if "-b /lib:/foo" is specified * and the symlink "/lib/a -> /lib/b" exists * in the host rootfs namespace, then it * should appear as "/foo/a -> /foo/b" in the * guest rootfs namespace for consistency * reasons. */ if (binding_referree != NULL) { comparison = compare_paths(binding_referree, binding_referrer); follow_binding = (comparison == PATHS_ARE_EQUAL); } } } else { sanity_check = true; follow_binding = true; } if (follow_binding) { switch (substitute_binding(tracee, HOST, path)) { case 0: return 0; case 1: return strlen(path) + 1; default: break; } } switch (compare_paths(get_root(tracee), path)) { case PATH1_IS_PREFIX: /* Remove the leading part, that is, the "root". */ prefix_length = strlen(get_root(tracee)); /* Special case when path to the guest rootfs == "/". */ if (prefix_length == 1) prefix_length = 0; new_length = strlen(path) - prefix_length; memmove(path, path + prefix_length, new_length); path[new_length] = '\0'; break; case PATHS_ARE_EQUAL: /* Special case when path == root. */ new_length = 1; strcpy(path, "/"); break; default: /* Ensure the path is within the new root. */ if (sanity_check) return -EPERM; else return 0; } return new_length + 1; }