diff options
author | Lauri Leukkunen <lle@rahina.org> | 2008-06-17 10:09:23 +0300 |
---|---|---|
committer | Lauri Leukkunen <lle@rahina.org> | 2008-06-17 10:09:23 +0300 |
commit | b26b8030110fd2f225412c26bed2f44849563ed0 (patch) | |
tree | 99caab52e9761c9ee5afef4566bdf3497ebabf7a | |
parent | 445f646df2614e0fa2aa6ba22da56c94e4d77b10 (diff) | |
parent | 312541a4e77f89def79c211d92010e533f3f5d30 (diff) |
Merge branch 'lle'1.99.0.25
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | debian/changelog | 4 | ||||
-rw-r--r-- | include/mapping.h | 9 | ||||
-rw-r--r-- | include/sb2.h | 1 | ||||
-rw-r--r-- | lua_scripts/argvenvp_gcc.lua | 4 | ||||
-rw-r--r-- | lua_scripts/mapping.lua | 207 | ||||
-rw-r--r-- | lua_scripts/pathmaps/maemo/00_default.lua | 38 | ||||
-rw-r--r-- | luaif/luaif.c | 19 | ||||
-rw-r--r-- | luaif/paths.c | 789 | ||||
-rw-r--r-- | luaif/sb_log.c | 3 | ||||
-rw-r--r-- | modeconf/gcc-specs.maemo | 9 | ||||
-rw-r--r-- | modeconf/sb2rc.maemo | 19 | ||||
-rw-r--r-- | preload/Makefile | 2 | ||||
-rwxr-xr-x | preload/gen-interface.pl | 17 | ||||
-rw-r--r-- | preload/glob.c | 1557 | ||||
-rw-r--r-- | preload/glob64.c | 29 | ||||
-rw-r--r-- | preload/interface.master | 79 | ||||
-rw-r--r-- | preload/libsb2.c | 121 | ||||
-rw-r--r-- | preload/libsb2.h | 17 | ||||
-rw-r--r-- | preload/sb_exec.c | 45 | ||||
-rw-r--r-- | utils/debconf2po-update | 9 | ||||
-rwxr-xr-x | utils/dpkg-checkbuilddeps | 1 | ||||
-rwxr-xr-x | utils/sb2 | 6 | ||||
-rwxr-xr-x | utils/sb2-init | 60 |
24 files changed, 2720 insertions, 328 deletions
@@ -23,7 +23,7 @@ endif CC = gcc CXX = g++ LD = ld -PACKAGE_VERSION = "1.99.0.24" +PACKAGE_VERSION = "1.99.0.25" PACKAGE = "SB2" LIBSB2_SONAME = "libsb2.so.1" LLBUILD ?= $(SRCDIR)/llbuild @@ -102,6 +102,7 @@ install-noarch: $(BUILD_TARGET) install -c -m 755 $(SRCDIR)/utils/deb-pkg-tools-wrapper $(prefix)/share/scratchbox2/scripts/dpkg install -c -m 755 $(SRCDIR)/utils/deb-pkg-tools-wrapper $(prefix)/share/scratchbox2/scripts/apt-get install -c -m 755 $(SRCDIR)/utils/dpkg-checkbuilddeps $(prefix)/share/scratchbox2/scripts/dpkg-checkbuilddeps + install -c -m 755 $(SRCDIR)/utils/debconf2po-update $(prefix)/share/scratchbox2/scripts/debconf2po-update install -c -m 755 $(SRCDIR)/utils/sb2-check-pkg-mappings $(prefix)/share/scratchbox2/scripts/sb2-check-pkg-mappings install -c -m 755 $(SRCDIR)/utils/sb2-exitreport $(prefix)/share/scratchbox2/scripts/sb2-exitreport install -c -m 755 $(SRCDIR)/utils/sb2-logz $(prefix)/bin/sb2-logz diff --git a/debian/changelog b/debian/changelog index 763baa8..aba6878 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,7 @@ +scratchbox2 (1.99.0.25) unstable; urgency=low + * fill me up + -- Lauri Aarnio <lauri.aarnio@iki.fi> Tue, 06 May 2008 17:50:00 +0300 + scratchbox2 (1.99.0.24) unstable; urgency=low * fill me up -- Lauri Leukkunen <lle@rahina.org> Fri, 14 Mar 2008 07:48:35 +0200 diff --git a/include/mapping.h b/include/mapping.h index f8cc0ba..5c3d916 100644 --- a/include/mapping.h +++ b/include/mapping.h @@ -10,8 +10,8 @@ #include <sys/types.h> -#define enable_mapping(a) a->mapping_disabled-- -#define disable_mapping(a) a->mapping_disabled++ +#define enable_mapping(a) ((a)->mapping_disabled--) +#define disable_mapping(a) ((a)->mapping_disabled++) enum lua_engine_states { LES_NOT_INITIALIZED = 0, @@ -20,9 +20,10 @@ enum lua_engine_states { }; extern char *scratchbox_path(const char *func_name, const char *path, - int *ro_flagp); + int *ro_flagp, int dont_resolve_final_symlink); extern char *scratchbox_path3(const char *binary_name, const char *func_name, - const char *path, const char *mapping_mode, int *ro_flagp); + const char *path, const char *mapping_mode, int *ro_flagp, + int dont_resolve_final_symlink); extern int sb_execve_mod(char **file, char ***argv, char ***envp); extern char *emumode_map(const char *path); diff --git a/include/sb2.h b/include/sb2.h index 1b897cd..a5fcef2 100644 --- a/include/sb2.h +++ b/include/sb2.h @@ -54,6 +54,7 @@ time_t get_sb2_timestamp(void); #define SB_LOGLEVEL_INFO 5 #define SB_LOGLEVEL_DEBUG 8 #define SB_LOGLEVEL_NOISE 9 +#define SB_LOGLEVEL_NOISE2 10 extern void sblog_init(void); extern void sblog_vprintf_line_to_logfile(const char *file, int line, diff --git a/lua_scripts/argvenvp_gcc.lua b/lua_scripts/argvenvp_gcc.lua index 48249a0..0f4fb34 100644 --- a/lua_scripts/argvenvp_gcc.lua +++ b/lua_scripts/argvenvp_gcc.lua @@ -38,6 +38,7 @@ gcc_tools = { gcc_bindir = os.getenv("SBOX_CROSS_GCC_DIR") gcc_subst_prefix = os.getenv("SBOX_CROSS_GCC_SUBST_PREFIX") gcc_extra_args = os.getenv("SBOX_EXTRA_CROSS_COMPILER_ARGS") +gcc_specs = os.getenv("SBOX_CROSS_GCC_SPECS_FILE") gcc_extra_stdinc = os.getenv("SBOX_EXTRA_CROSS_COMPILER_STDINC") gcc_block_args = os.getenv("SBOX_BLOCK_CROSS_COMPILER_ARGS") ld_extra_args = os.getenv("SBOX_EXTRA_CROSS_LD_ARGS") @@ -55,6 +56,9 @@ for prefix in string.gmatch(":" .. os.getenv("SBOX_CROSS_GCC_PREFIX_LIST"), "[^: tmp.new_filename = gcc_bindir .. "/" .. gcc_subst_prefix .. gcc_compilers[i] tmp.add_tail = {} tmp.remove = {} + if (gcc_specs) then + table.insert(tmp.add_tail, "-specs="..gcc_specs) + end if (gcc_extra_args) then for gcc_extra in string.gmatch(gcc_extra_args, "[^ ]+") do table.insert(tmp.add_tail, gcc_extra) diff --git a/lua_scripts/mapping.lua b/lua_scripts/mapping.lua index 3aa5a46..f638ca8 100644 --- a/lua_scripts/mapping.lua +++ b/lua_scripts/mapping.lua @@ -21,6 +21,7 @@ end debug = os.getenv("SBOX_MAPPING_DEBUG") +debug_messages_enabled = sb.debug_messages_enabled() -- SBOX_LUA_SCRIPTS environment variable controls where -- we look for the scriptlets defining the path mappings @@ -156,84 +157,39 @@ for m = 1, table.maxn(mm) do end end -function adjust_for_mapping_leakage(path, leakage_prefix) - if (not path) then - return nil - end - - if (not isprefix(leakage_prefix, path)) then - -- The mapping result is not - -- expected to be inside leakage_prefix. - return path - end - - local tmp = sb.readlink(path) - if (not tmp) then - -- not a symlink - return path - end - - -- make it an absolute path if it's not - if (string.sub(tmp, 1, 1) ~= "/") then - tmp = dirname(path) .. "/" .. tmp - end - - if (sb.decolonize_path(tmp) == sb.decolonize_path(path)) then - -- symlink refers to itself - return path - end - - tmp = sb.decolonize_path(tmp) - - if (not isprefix(leakage_prefix, tmp)) then - -- aha! tried to get out of there, now map it right back in - return adjust_for_mapping_leakage(leakage_prefix .. tmp, leakage_prefix) - else - return adjust_for_mapping_leakage(tmp, leakage_prefix) - end -end - -no_adjust_funcs = { - "__lxstat", - "__lxstat64", - "__xmknod", - "lchmod", - "lchown", - "lgetxattr", - "llistxattr", - "lremovexattr", - "lsetxattr", - "lstat", - "lstat64", - "lutimes", - "readlink", - "rename", - "renameat", - "symlink", - "symlinkat", - "unlink", - "unlinkat" -} - -function should_adjust(func_name) - for i = 1, table.maxn(no_adjust_funcs) do - if (no_adjust_funcs[i] == func_name) then - return false - end - end - return true -end - function sbox_execute_replace_rule(path, replacement, rule) local ret = nil - sb.log("debug", string.format("replace:%s:%s", path, replacement)) + if (debug_messages_enabled) then + sb.log("debug", string.format("replace:%s:%s", path, replacement)) + end if (rule.prefix) then - ret = replacement .. string.sub(path, string.len(rule.prefix)+1) - sb.log("debug", string.format("replaced (prefix) => %s", ret)) + -- "path" may be shorter than prefix during path resolution + if ((rule.prefix ~= "") and + (isprefix(rule.prefix, path))) then + ret = replacement .. string.sub(path, string.len(rule.prefix)+1) + if (debug_messages_enabled) then + sb.log("debug", string.format("replaced (prefix) => %s", ret)) + end + else + ret = "" + if (debug_messages_enabled) then + sb.log("debug", string.format("replacement failed (short path?)")) + end + end elseif (rule.path) then - ret = replacement - sb.log("debug", string.format("replaced (path) => %s", ret)) + -- "path" may be shorter than prefix during path resolution + if (rule.path == path) then + ret = replacement + if (debug_messages_enabled) then + sb.log("debug", string.format("replaced (path) => %s", ret)) + end + else + ret = "" + if (debug_messages_enabled) then + sb.log("debug", string.format("replacement failed (short path?)")) + end + end else sb.log("error", "error in rule: can't replace without 'prefix' or 'path'") ret = path @@ -249,7 +205,9 @@ function sbox_execute_conditional_actions(binary_name, local a for a = 1, table.maxn(actions) do - sb.log("debug", string.format("try %d", a)) + if (debug_messages_enabled) then + sb.log("debug", string.format("try %d", a)) + end local ret_ro = false if (actions[a].readonly) then @@ -274,7 +232,9 @@ function sbox_execute_conditional_actions(binary_name, end if (tmp_dest ~= nil) then if (sb.path_exists(tmp_dest)) then - sb.log("debug", string.format("target exists: => %s", tmp_dest)) + if (debug_messages_enabled) then + sb.log("debug", string.format("target exists: => %s", tmp_dest)) + end return tmp_dest, ret_ro end else @@ -309,21 +269,18 @@ function sbox_execute_rule(binary_name, func_name, work_dir, rp, path, rule) sb.log("error", "mapping rule uses does not have any valid actions, path="..path) end - if (should_adjust(func_name)) then - if (isprefix(target_root, ret_path)) then - ret_path = adjust_for_mapping_leakage(ret_path, target_root) - elseif (isprefix(tools_root, ret_path)) then - ret_path = adjust_for_mapping_leakage(ret_path, tools_root) - end - end - return ret_path, ret_ro end - -function find_rule(chain, func, path) +-- returns rule and min_path_len, minimum length which is needed for +-- successfull mapping. +function find_rule(chain, func, full_path) local i = 0 local wrk = chain + local min_path_len = 0 + if (debug_messages_enabled) then + sb.log("noise", string.format("find_rule for (%s)", full_path)) + end while (wrk) do -- travel the chains for i = 1, table.maxn(wrk.rules) do @@ -334,20 +291,34 @@ function find_rule(chain, func, path) -- compare prefix (only if a non-zero prefix) if (wrk.rules[i].prefix and (wrk.rules[i].prefix ~= "") and - (isprefix(wrk.rules[i].prefix, path))) then - return wrk.rules[i] + (isprefix(wrk.rules[i].prefix, full_path))) then + if (debug_messages_enabled) then + sb.log("noise", string.format("selected prefix rule %d (%s)", i, wrk.rules[i].prefix)) + end + min_path_len = string.len(wrk.rules[i].prefix) + return wrk.rules[i], min_path_len end -- "path" rules: (exact match) - if (wrk.rules[i].path == path) then - return wrk.rules[i] + if (wrk.rules[i].path == full_path) then + if (debug_messages_enabled) then + sb.log("noise", string.format("selected path rule %d (%s)", i, wrk.rules[i].path)) + end + min_path_len = string.len(wrk.rules[i].path) + return wrk.rules[i], min_path_len end -- "match" rules use a lua "regexp". -- these will be obsoleted, as this kind of rule -- is almost impossible to reverse (backward mapping -- is not possible as long as there are "match" rules) if (wrk.rules[i].match) then - if (string.match(path, wrk.rules[i].match)) then - return wrk.rules[i] + if (string.match(full_path, wrk.rules[i].match)) then + if (debug_messages_enabled) then + sb.log("noise", string.format("selected match rule %d (%s)", i, wrk.rules[i].match)) + end + -- there is no easy and reliable + -- way to determine min_path_len + -- so leave it to zero + return wrk.rules[i], min_path_len end end -- FIXME: Syntax checking should be added: @@ -357,17 +328,20 @@ function find_rule(chain, func, path) end wrk = wrk.next_chain end - return nil + if (debug_messages_enabled) then + sb.log("noise", string.format("rule not found")) + end + return nil, 0 end -function map_using_chain(chain, binary_name, func_name, work_dir, path) +function map_using_chain(chain, binary_name, func_name, work_dir, path, full_path) local ret = path local rp = path local rule = nil local readonly_flag = false - rule = find_rule(chain, func_name, rp) + rule = find_rule(chain, func_name, full_path) if (not rule) then -- error, not even a default rule found sb.log("error", string.format("Unable to find a match at all: %s(%s)", func_name, path)) @@ -391,28 +365,23 @@ function map_using_chain(chain, binary_name, func_name, work_dir, path) end else ret, readonly_flag = sbox_execute_rule(binary_name, func_name, work_dir, rp, path, rule) - if (debug) then - if(path == ret) then - -- sb.log("debug", string.format("[%s][%s] %s(%s) [==]", basename(rule.lua_script), rule.binary_name, func_name, path)) - else - -- sb.log("debug", string.format("[%s][%s] %s(%s) -> (%s)", basename(rule.lua_script), rule.binary_name, func_name, path, ret)) - end - end end return ret, readonly_flag end -- sbox_translate_path is the function called from libsb2.so -- preload library and the FUSE system for each path that needs --- translating +-- translating. +-- Note that for path resolution phase, "path" is a prefix of "full_path" +-- (full_path should be used to select the rule) -- returns path and the "readonly" flag -function sbox_translate_path(mapping_mode, binary_name, func_name, work_dir, path) +function sbox_translate_path(mapping_mode, binary_name, func_name, work_dir, path, full_path) -- loop through the chains, first match is used for n=1,table.maxn(modes[mapping_mode].chains) do if (not modes[mapping_mode].chains[n].noentry and (not modes[mapping_mode].chains[n].binary or binary_name == modes[mapping_mode].chains[n].binary)) then - return map_using_chain(modes[mapping_mode].chains[n], binary_name, func_name, work_dir, path) + return map_using_chain(modes[mapping_mode].chains[n], binary_name, func_name, work_dir, path, full_path) end end -- we should never ever get here, if we still do, don't do anything @@ -422,3 +391,33 @@ function sbox_translate_path(mapping_mode, binary_name, func_name, work_dir, pat return path, false end +-- sbox_get_mapping_requirements is called from libsb2.so before +-- path resolution takes place. The primary purpose of this is to +-- determine where to start resolving symbolic links; shorter paths than +-- "min_path_len" should not be given to sbox_translate_path() +-- returns "rule_found", "min_path_len" +function sbox_get_mapping_requirements(mapping_mode, binary_name, func_name, work_dir, full_path) + -- loop through the chains, first match is used + local min_path_len = 0 + local rule = nil + for n=1,table.maxn(modes[mapping_mode].chains) do + if (not modes[mapping_mode].chains[n].noentry + and (not modes[mapping_mode].chains[n].binary + or binary_name == modes[mapping_mode].chains[n].binary)) then + rule, min_path_len = find_rule(modes[mapping_mode].chains[n], func_name, full_path) + if (not rule) then + -- error, not even a default rule found + sb.log("error", string.format("Unable to find rule for: %s(%s)", func_name, full_path)) + return false, 0 + end + + return true, min_path_len + + end + end + sb.log("error", string.format("Unable to find chain+rule for: %s(%s)", + func_name, full_path)) + + return false, 0 +end + diff --git a/lua_scripts/pathmaps/maemo/00_default.lua b/lua_scripts/pathmaps/maemo/00_default.lua index cee688c..023333d 100644 --- a/lua_scripts/pathmaps/maemo/00_default.lua +++ b/lua_scripts/pathmaps/maemo/00_default.lua @@ -35,6 +35,8 @@ simple_chain = { use_orig_path = true, readonly = true}, {prefix = "/opt/maemo", use_orig_path = true, readonly = true}, + {prefix = "/usr/share/scratchbox2/host_usr", + replace_by = "/usr", readonly = true}, {prefix = "/usr/share/scratchbox2", use_orig_path = true, readonly = true}, @@ -53,7 +55,7 @@ simple_chain = { {path = "/bin/sh", replace_by = tools .. "/bin/bash", readonly = true}, - {path = "/usr/bin/host-", + {prefix = "/usr/bin/host-", use_orig_path = true, readonly = true}, -- ----------------------------------------------- @@ -80,7 +82,8 @@ simple_chain = { -- ----------------------------------------------- -- 45. /usr/share/aclocal* - -- This is a bit complex, we must mix files from both places: + -- This is more than a bit complex, we must mix files from + -- both places: -- Prefer files in tools_root, but if not there, try -- to get it from target_root. New files will be created -- to target_root. @@ -102,6 +105,16 @@ simple_chain = { actions = test_first_target_then_tools_default_is_target}, {path = "/usr/share/aclocal-1.10", actions = test_first_target_then_tools_default_is_target}, + + -- Next, exceptions to these rules: + -- 1) gnome-common presents policy problems, typically we + -- have it in both places but want to take it from the + -- rootstrap: + {prefix = "/usr/share/aclocal/gnome-common", + actions = test_first_target_then_tools_default_is_target}, + {prefix = "/usr/share/aclocal/gnome-compiler", + actions = test_first_target_then_tools_default_is_target}, + -- Next, use /usr/share/aclocal* from tools_root if target -- exists, but default is target_root {prefix = "/usr/share/aclocal", @@ -114,6 +127,10 @@ simple_chain = { -- but there are lots of exceptions. That directory -- is used for so many purposes nowadays.. + -- (see the comment about gnome-common files in .../aclocal): + {prefix = "/usr/share/gnome-common", + actions = test_first_target_then_tools_default_is_target}, + {prefix = "/usr/share/glib-2.0", map_to = target_root}, {prefix = "/usr/share/dbus-1", map_to = target_root}, @@ -223,16 +240,33 @@ simple_chain = { -- 98. Scratchbox 1 emulation rules -- (some packages have hard-coded paths to the SB1 enviroment; -- replace those by the correct locations in our environment) + + -- "libtool" for arm {prefix = "/scratchbox/compilers/cs2005q3.2-glibc2.5-arm/arch_tools/share/libtool", replace_by = sb2_share_dir .. "/libtool", log_level = "warning", readonly = true}, + -- "libtool" for i386 + {prefix = "/scratchbox/compilers/cs2005q3.2-glibc-i386/arch_tools/share", + replace_by = tools .. "/usr/share", + log_level = "warning", + readonly = true}, + {prefix = "/scratchbox/tools/bin", replace_by = tools .. "/usr/bin", log_level = "warning", readonly = true}, + {prefix = "/scratchbox/tools/autotools/automake-1.7/share/automake-1.7", + replace_by = tools .. "/usr/share/automake-1.7", + log_level = "warning", + readonly = true}, + + -- otherwise, don't map /scratchbox, some people still + -- keep their projects there. + {prefix = "/scratchbox", use_orig_path = true}, + -- ----------------------------------------------- -- 100. DEFAULT RULES: -- the root directory itself must not be mapped: diff --git a/luaif/luaif.c b/luaif/luaif.c index 2319c92..411bb20 100644 --- a/luaif/luaif.c +++ b/luaif/luaif.c @@ -332,6 +332,10 @@ static int lua_sb_log(lua_State *luastate) SB_LOG(SB_LOGLEVEL_WARNING, "WARNING: %s", logmsg); else if(!strcmp(loglevel, "error")) SB_LOG(SB_LOGLEVEL_ERROR, "ERROR: %s", logmsg); + else if(!strcmp(loglevel, "noise")) + SB_LOG(SB_LOGLEVEL_NOISE, ">>>>: %s", logmsg); + else if(!strcmp(loglevel, "noise2")) + SB_LOG(SB_LOGLEVEL_NOISE2, ">>>>>>: %s", logmsg); else /* default to level "error" */ SB_LOG(SB_LOGLEVEL_ERROR, "%s", logmsg); @@ -387,6 +391,20 @@ static int lua_sb_path_exists(lua_State *l) return 1; } +/* "sb.debug_messages_enabled", to be called from lua code + * returns true if SB_LOG messages have been enabled for the debug levels + * (debug,noise,noise2...) +*/ +static int lua_sb_debug_messages_enabled(lua_State *l) +{ + if (SB_LOG_IS_ACTIVE(SB_LOGLEVEL_DEBUG)) { + lua_pushboolean(l, 1); + } else { + lua_pushboolean(l, 0); + } + return 1; +} + /* mappings from c to lua */ static const luaL_reg reg[] = { @@ -396,6 +414,7 @@ static const luaL_reg reg[] = {"log", lua_sb_log}, {"setenv", lua_sb_setenv}, {"path_exists", lua_sb_path_exists}, + {"debug_messages_enabled", lua_sb_debug_messages_enabled}, {NULL, NULL} }; diff --git a/luaif/paths.c b/luaif/paths.c index a8a9b96..92d5079 100644 --- a/luaif/paths.c +++ b/luaif/paths.c @@ -1,8 +1,28 @@ /* * Copyright (C) 2006,2007 Lauri Leukkunen <lle@rahina.org> + * Portion Copyright (c) 2008 Nokia Corporation. + * (symlink- and path resolution code refactored by Lauri T. Aarnio at Nokia) * * Licensed under LGPL version 2.1, see top level LICENSE file for details. - */ + * + * ---------------- + * + * This file implements path resolution for SB2. Please read background + * information from the "path_resolution" manual page (Linux documentation). + * + * Path resolution usually belongs to the operating system, but SB2 must + * implement a replacement because of one specific feature: Symbolic + * links must be mapped. For example, if path "/a/b/c" contains + * intermediate symbolic links (i.e. "a" or "b" is a symlink), the path + * mapping engine must be called for those all of those symbolic links. + * + * The division between Lua and C code is that + * - C has been used for parts that must follow "standardized" or "fixed" + * approach, and the implementation is not expected to be changed, in other + * words, the implementation/design freedom is restricted by external + * requirements. + * - Lua has been used for the mapping algorithm itself. +*/ #include <unistd.h> #include <stdio.h> @@ -35,6 +55,8 @@ #include <mapping.h> #include <sb2.h> +#include "libsb2.h" +#include "exported.h" #ifdef EXTREME_DEBUGGING #include <execinfo.h> @@ -42,36 +64,198 @@ extern int lua_engine_state; +/* ========== Path & Path component handling primitives: ========== */ + struct path_entry { - struct path_entry *prev; - struct path_entry *next; - char name[PATH_MAX]; + struct path_entry *pe_prev; + struct path_entry *pe_next; + char *pe_last_component_name; + char pe_full_path[PATH_MAX]; }; -char *sb_decolonize_path(const char *path) +struct path_entry_list { + struct path_entry *pl_first; +}; + +static char *path_entries_to_string(char *buf, struct path_entry *work) +{ + *buf = '\0'; + if (!work) { + /* "work" will be empty if orig.path was "/." */ + strcat(buf, "/"); + } else while (work) { + strcat(buf, "/"); + strcat(buf, work->pe_last_component_name); + work = work->pe_next; + } + + return(buf); +} + +static void free_path_entries(struct path_entry_list *listp) { - char *cpath, *index, *start; - char cwd[PATH_MAX + 1]; - struct path_entry list; struct path_entry *work; - struct path_entry *new; - char *buf = NULL; - if (!path) { - SB_LOG(SB_LOGLEVEL_ERROR, - "sb_decolonize_path called with NULL path"); - return NULL; + work = listp->pl_first; + + while (work) { + struct path_entry *tmp; + tmp = work; + work = work->pe_next; + free(tmp); } - buf = malloc((PATH_MAX + 1) * sizeof(char)); - memset(buf, '\0', PATH_MAX + 1); + listp->pl_first = NULL; +} + +static void split_path_to_path_entries( + char *cpath, /* path buffer; will be modified */ + struct path_entry_list *listp) +{ + struct path_entry *work = NULL; + char *start; + char tmp_path_buf[PATH_MAX+1]; - list.next = NULL; - list.prev = NULL; - work = &list; + SB_LOG(SB_LOGLEVEL_NOISE2, "going to split '%s'", cpath); - if (path[0] != '/') { + listp->pl_first = NULL; + + start = cpath; + while(*start == '/') start++; /* ignore leading '/' */ + + while (1) { + unsigned int last = 0; + char *index; + + index = strstr(start, "/"); + if (!index) { + last = 1; + } else { + *index = '\0'; + } + + /* ignore empty strings resulting from // */ + if (index != start) { + struct path_entry *new; + + /* add an entry to our path_entry list */ + if (!(new = calloc(1,sizeof(struct path_entry)))) + abort(); + + new->pe_prev = work; + if(work) work->pe_next = new; + + new->pe_next = NULL; + strcpy(new->pe_full_path, cpath); + new->pe_last_component_name = new->pe_full_path + (start-cpath); + work = new; + if(!listp->pl_first) listp->pl_first = work; + SB_LOG(SB_LOGLEVEL_NOISE2, + "created entry 0x%X '%s' '%s'", + (int)work, work->pe_full_path, start); + } + + if (last) + break; + *index = '/'; + start = index + 1; + } + SB_LOG(SB_LOGLEVEL_NOISE2, + "split->'%s'", path_entries_to_string(tmp_path_buf, listp->pl_first)); +} + +/* remove a path_entry from list, return pointer to the next + * path_entry after the removed one (NULL if the removed entry was last) +*/ +static struct path_entry *remove_path_entry( + struct path_entry_list *listp, + struct path_entry *p_entry) /* entry to be removed */ +{ + struct path_entry *ret = p_entry->pe_next; + + if (p_entry->pe_prev) { + /* not the first element in the list */ + p_entry->pe_prev->pe_next = p_entry->pe_next; + if(p_entry->pe_next) + p_entry->pe_next->pe_prev = p_entry->pe_prev; + } else { + /* removing first element from the list */ + assert(p_entry == listp->pl_first); + listp->pl_first = p_entry->pe_next; + if(p_entry->pe_next) + p_entry->pe_next->pe_prev = NULL; + } + free(p_entry); + return(ret); +} + +static void remove_last_path_entry(struct path_entry_list *listp) +{ + struct path_entry *work; + + work = listp->pl_first; + + while (work && work->pe_next) { + work = work->pe_next; + } + if (work) { + /* now "work" points to last element in the list */ + remove_path_entry(listp, work); + } +} + +static void remove_dots_and_dotdots_from_path_entries( + struct path_entry_list *listp) +{ + struct path_entry *work; + char tmp_path_buf[PATH_MAX+1]; + + work = listp->pl_first; + + SB_LOG(SB_LOGLEVEL_NOISE2, + "remove_dots_and_dotdots: Clean ->'%s'", + path_entries_to_string(tmp_path_buf, listp->pl_first)); + while (work) { + SB_LOG(SB_LOGLEVEL_NOISE2, + "remove_dots_and_dotdots: work=0x%X examine '%s'", + (int)work, work?work->pe_last_component_name:""); + if (strcmp(work->pe_last_component_name, "..") == 0) { + struct path_entry *dotdot = work; + struct path_entry *preventry = work->pe_prev; + + if (preventry) { + /* travel up, and eliminate previous name */ + work = remove_path_entry(listp, preventry); + assert(work == dotdot); + } else { + /* no preventry, first component is .. */ + assert(work == listp->pl_first); + } + work = remove_path_entry(listp, dotdot); + } else if (strcmp(work->pe_last_component_name, ".") == 0) { + /* ignore this node */ + work = remove_path_entry(listp, work); + } else { + work = work->pe_next; + } + } + SB_LOG(SB_LOGLEVEL_NOISE2, + "remove_dots_and_dotdots: cleaned->'%s'", + path_entries_to_string(tmp_path_buf, listp->pl_first)); +} + +static char *absolute_path(const char *path) +{ + char *cpath = NULL; + + if (path[0] == '/') { + /* already absolute path */ + if (!(cpath = strdup(path))) + abort(); + } else { /* not an absolute path */ + char cwd[PATH_MAX + 1]; + memset(cwd, '\0', sizeof(cwd)); if (!getcwd(cwd, sizeof(cwd))) { /* getcwd() returns NULL if the path is really long. @@ -80,7 +264,7 @@ char *sb_decolonize_path(const char *path) * must not fail! */ SB_LOG(SB_LOGLEVEL_ERROR, - "sb_decolonize_path failed to get current dir" + "absolute_path failed to get current dir" " (processing continues with relative path)"); if (!(cpath = strdup(path))) abort(); @@ -89,89 +273,436 @@ char *sb_decolonize_path(const char *path) if (!cpath) abort(); } - } else { - if (!(cpath = strdup(path))) - abort(); + SB_LOG(SB_LOGLEVEL_NOISE, "absolute_path done, '%s'", cpath); } + return(cpath); +} - start = cpath; - while(*start == '/') start++; /* ignore leading '/' */ +char *sb_decolonize_path(const char *path) +{ + char *cpath; + struct path_entry_list list; + char *buf = NULL; - while (1) { - unsigned int last = 0; + if (!path) { + SB_LOG(SB_LOGLEVEL_ERROR, + "sb_decolonize_path called with NULL path"); + return NULL; + } + SB_LOG(SB_LOGLEVEL_NOISE, "sb_decolonize_path '%s'", path); - index = strstr(start, "/"); - if (!index) { - last = 1; + list.pl_first = NULL; + + cpath = absolute_path(path); + + split_path_to_path_entries(cpath, &list); + remove_dots_and_dotdots_from_path_entries(&list); + + buf = malloc((PATH_MAX + 1) * sizeof(char)); + memset(buf, '\0', PATH_MAX + 1); + + path_entries_to_string(buf, list.pl_first); + free_path_entries(&list); + + SB_LOG(SB_LOGLEVEL_NOISE, "sb_decolonize_path returns '%s'", buf); + return buf; +} + +/* dirname() is not thread safe (may return pointer to static buffer), + * so we'll have our own version, which always returns absolute dirnames: +*/ +static char *sb_abs_dirname(const char *path) +{ + char *cpath; + struct path_entry_list list; + char *buf = NULL; + + if (!path) return strdup("."); + SB_LOG(SB_LOGLEVEL_NOISE, "sb_abs_dirname '%s'", path); + + list.pl_first = NULL; + + cpath = absolute_path(path); + + split_path_to_path_entries(cpath, &list); + remove_last_path_entry(&list); + + buf = malloc((PATH_MAX + 1) * sizeof(char)); + memset(buf, '\0', PATH_MAX + 1); + + path_entries_to_string(buf, list.pl_first); + free_path_entries(&list); + + SB_LOG(SB_LOGLEVEL_NOISE, "sb_abs_dirname returns '%s'", buf); + return buf; +} + +/* ========== Other helper functions: ========== */ + +static char last_char_in_str(const char *str) +{ + if (!str || !*str) return('\0'); + while (str[1]) str++; + return(*str); +} + +/* ========== Interfaces to Lua functions: ========== */ + +static char *call_lua_function_sbox_translate_path( + int result_log_level, + struct lua_instance *luaif, + const char *mapping_mode, + const char *binary_name, + const char *func_name, + const char *work_dir, + const char *decolon_path, + int *ro_flagp, + const char *full_path_for_rule_selection) +{ + int ro_flag; + char *traslate_result = NULL; + + SB_LOG(SB_LOGLEVEL_NOISE, "calling sbox_translate_path for %s(%s,%s)", + func_name, decolon_path, full_path_for_rule_selection); + + lua_getfield(luaif->lua, LUA_GLOBALSINDEX, "sbox_translate_path"); + lua_pushstring(luaif->lua, mapping_mode); + lua_pushstring(luaif->lua, binary_name); + lua_pushstring(luaif->lua, func_name); + lua_pushstring(luaif->lua, work_dir); + lua_pushstring(luaif->lua, decolon_path); + lua_pushstring(luaif->lua, full_path_for_rule_selection); + lua_call(luaif->lua, 6, 2); /* six arguments, returns path+ro_flag */ + + traslate_result = (char *)lua_tostring(luaif->lua, -2); + if (traslate_result) { + traslate_result = strdup(traslate_result); + } + ro_flag = lua_toboolean(luaif->lua, -1); + if (ro_flagp) *ro_flagp = ro_flag; + lua_pop(luaif->lua, 2); + + if (traslate_result) { + /* sometimes a mapping rule may create paths that contain + * doubled slashes ("//") or end with a slash. We'll + * need to clean the path here. + */ + char *cleaned_path; + + cleaned_path = sb_decolonize_path(traslate_result); + free(traslate_result); + traslate_result = NULL; + + /* log the result */ + if (strcmp(cleaned_path, decolon_path) == 0) { + /* NOTE: Following SB_LOG() call is used by the log + * postprocessor script "sb2logz". Do not change + * without making a corresponding change to + * the script! + */ + SB_LOG(result_log_level, "pass: %s '%s'%s", + func_name, decolon_path, + (ro_flag ? " (readonly)" : "")); } else { - *index = '\0'; + /* NOTE: Following SB_LOG() call is used by the log + * postprocessor script "sb2logz". Do not change + * without making a corresponding change to + * the script! + */ + SB_LOG(result_log_level, "mapped: %s '%s' -> '%s'%s", + func_name, decolon_path, cleaned_path, + (ro_flag ? " (readonly)" : "")); } + return cleaned_path; + } + SB_LOG(SB_LOGLEVEL_ERROR, + "No result from sbox_translate_path for: %s '%s'", + func_name, decolon_path); + return(NULL); +} - if (index == start) { - goto proceed; /* skip over empty strings - resulting from // */ - } +/* - returns 1 if ok (then *min_path_lenp is valid) + * - returns 0 if failed to find the rule +*/ +static int call_lua_function_sbox_get_mapping_requirements( + struct lua_instance *luaif, + const char *mapping_mode, + const char *binary_name, + const char *func_name, + const char *work_dir, + const char *full_path_for_rule_selection, + int *min_path_lenp) +{ + int rule_found; + int min_path_len; - if (strcmp(start, "..") == 0) { - /* travel up one */ - if (!work->prev) - goto proceed; - work = work->prev; - free(work->next); - work->next = NULL; - } else if (strcmp(start, ".") == 0) { - /* ignore */ - goto proceed; - } else { - /* add an entry to our path_entry list */ - if (!(new = malloc(sizeof(struct path_entry)))) - abort(); - memset(new->name, '\0', PATH_MAX); - new->prev = work; - work->next = new; - new->next = NULL; - strcpy(new->name, start); - work = new; + SB_LOG(SB_LOGLEVEL_NOISE, + "calling sbox_get_mapping_requirements for %s(%s)", + func_name, full_path_for_rule_selection); + + lua_getfield(luaif->lua, LUA_GLOBALSINDEX, + "sbox_get_mapping_requirements"); + lua_pushstring(luaif->lua, mapping_mode); + lua_pushstring(luaif->lua, binary_name); + lua_pushstring(luaif->lua, func_name); + lua_pushstring(luaif->lua, work_dir); + lua_pushstring(luaif->lua, full_path_for_rule_selection); + /* five arguments, returns flag+min_path_len */ + lua_call(luaif->lua, 5, 2); + + rule_found = lua_toboolean(luaif->lua, -2); + min_path_len = lua_tointeger(luaif->lua, -1); + if (min_path_lenp) *min_path_lenp = min_path_len; + lua_pop(luaif->lua, 2); + + SB_LOG(SB_LOGLEVEL_DEBUG, "sbox_get_mapping_requirements -> %d,%d", + rule_found, min_path_len); + + return(rule_found); +} + +/* ========== Path resolution: ========== + * This is the place where symlinks are followed. + * Returns an allocated buffer containing the resolved path (or NULL if error) + * + * FIXME: It might be possible to eliminate parameter + * "full_path_for_rule_selection" now when the separate call to + * sbox_get_mapping_requirements is used to ensure that "path" is long + * enough. However, this has been left to be done in the future, together + * with other optimizations. +*/ +static char *sb_path_resolution( + int nest_count, + struct lua_instance *luaif, + const char *mapping_mode, + const char *binary_name, + const char *func_name, + const char *work_dir, + const char *path, + const char *full_path_for_rule_selection, + int dont_resolve_final_symlink) +{ + char *cpath; + struct path_entry_list orig_path_list; + char *buf = NULL; + struct path_entry *work; + int component_index = 0; + int min_path_len_to_check; + + if (nest_count > 16) { + SB_LOG(SB_LOGLEVEL_ERROR, + "sb_path_resolution: too deep nesting " + "(too many symbolic links"); + + /* FIXME: This should return ELOOP to the calling program, + * but that does not happen currently, because there is no + * proper way to signal this kind of failures in the mapping + * phase. This is somewhat complex to fix; the fix requires + * that the mapping engine interface and other places must + * be changed, too (e.g. the interface generator, etc). + * This is minor problem currently. + */ + errno = ELOOP; + return NULL; + } + + if (!path) { + SB_LOG(SB_LOGLEVEL_ERROR, + "sb_path_resolution called with NULL path"); + return NULL; + } + + SB_LOG(SB_LOGLEVEL_NOISE, + "sb_path_resolution %d '%s'", nest_count, path); + + orig_path_list.pl_first = NULL; + + cpath = absolute_path(path); + + split_path_to_path_entries(cpath, &orig_path_list); + + work = orig_path_list.pl_first; + + if (call_lua_function_sbox_get_mapping_requirements( + luaif, mapping_mode, binary_name, func_name, + work_dir, full_path_for_rule_selection, + &min_path_len_to_check)) { + /* has requirements: + * skip over path components that we are not supposed to check, + * because otherwise rule recognition & execution could fail. + */ + while ((int)strlen(work->pe_full_path) < min_path_len_to_check) { + SB_LOG(SB_LOGLEVEL_NOISE2, "skipping [%d] '%s'", + component_index, work->pe_last_component_name); + component_index++; + work = work->pe_next; } + } -proceed: - if (last) + /* Path resolution loop = walk thru directories, and if a symlink + * is found, recurse.. + */ + while (work) { + char link_dest[PATH_MAX]; + char decolon_tmp[PATH_MAX]; + int link_len; + char *prefix_mapping_result; + struct path_entry_list prefix_path_list; + int ro_tmp; + + if (dont_resolve_final_symlink && (work->pe_next == NULL)) { + /* this is last component, but here a final symlink + * must not be resolved (calls like lstat(), rename(), + * etc) + */ + SB_LOG(SB_LOGLEVEL_NOISE2, + "Won't check last component [%d] '%s'", + component_index, work->pe_full_path); break; - *index = '/'; - start = index + 1; - } + } - work = list.next; - if (!work) { - /* "work" will be empty if orig.path was "/." */ - strcat(buf, "/"); - } else while (work) { - struct path_entry *tmp; - strcat(buf, "/"); - strcat(buf, work->name); - tmp = work; - work = work->next; - free(tmp); + SB_LOG(SB_LOGLEVEL_NOISE2, "test [%d] '%s'", + component_index, work->pe_full_path); + + prefix_path_list.pl_first = NULL; + split_path_to_path_entries(work->pe_full_path, &prefix_path_list); + remove_dots_and_dotdots_from_path_entries(&prefix_path_list); + path_entries_to_string(decolon_tmp, prefix_path_list.pl_first); + free_path_entries(&prefix_path_list); + + prefix_mapping_result = call_lua_function_sbox_translate_path( + SB_LOGLEVEL_NOISE, + luaif, mapping_mode, binary_name, "PATH_RESOLUTION", + work_dir, decolon_tmp, &ro_tmp, + full_path_for_rule_selection); + + SB_LOG(SB_LOGLEVEL_NOISE2, "prefix_mapping_result='%s'", + prefix_mapping_result); + + /* determine if "prefix_mapping_result" is a symbolic link. + * this can't be done with lstat(), because lstat() does not + * exist as a function on Linux => lstat_nomap() can not be + * used eiher. fortunately readlink() is an ordinary function. + */ + link_len = readlink_nomap(prefix_mapping_result, link_dest, PATH_MAX); + + if (link_len > 0) { + /* was a symlink */ + char new_path[PATH_MAX]; + char rest_of_path[PATH_MAX]; + + link_dest[link_len] = '\0'; + if (work->pe_next) { + path_entries_to_string(rest_of_path, work->pe_next); + } else { + /* last component of the path was a symlink. */ + *rest_of_path = '\0'; + } + SB_LOG(SB_LOGLEVEL_NOISE, + "is symlink: rest='%s'", rest_of_path); + + if (*link_dest == '/') { + /* absolute symlink. + * This is easy: just join the symlink + * and rest of path, and further mapping + * operations will take care of the rest. + */ + SB_LOG(SB_LOGLEVEL_NOISE, + "absolute symlink at '%s' " + "points to '%s', restarting", + prefix_mapping_result, link_dest); + strcpy(new_path, link_dest); + if (*rest_of_path) { + if ((last_char_in_str(new_path) != '/') + && (*rest_of_path != '/')) { + strcat(new_path, "/"); + } + strcat(new_path, rest_of_path); + } + } else { + /* A relative symlink. Somewhat complex: + * "prefix_mapping_result" contains the + * real location in the FS, but here we + * must still build the full path from the + * place where we pretend to be - otherwise + * path mapping code would fail to find the + * correct location. Hence "dirnam" is + * based on what was mapped, and not based on + * were the mapping took us. + */ + char *dirnam; + + dirnam = sb_abs_dirname(work->pe_full_path); + + SB_LOG(SB_LOGLEVEL_NOISE, + "relative symlink at '%s' " + "points to '%s'", + prefix_mapping_result, link_dest); + strcpy(new_path, dirnam); + if (last_char_in_str(new_path) != '/') + strcat(new_path, "/"); + strcat(new_path, link_dest); + if (*rest_of_path) { + if ((last_char_in_str(new_path) != '/') + && (*rest_of_path != '/')) { + strcat(new_path, "/"); + } + strcat(new_path, rest_of_path); + } + free(dirnam); + } + free(prefix_mapping_result); + free_path_entries(&orig_path_list); + + /* recursively call myself to perform path + * resolution steps for the symlink target. + */ + return(sb_path_resolution(nest_count + 1, + luaif, mapping_mode, binary_name, func_name, + work_dir, new_path, + new_path, dont_resolve_final_symlink)); + } + free(prefix_mapping_result); + work = work->pe_next; + component_index++; } + + /* All symbolic links have been resolved. + * + * Since there are no symlinks in "orig_path_list", "." and ".." + * entries can be safely removed: + */ + remove_dots_and_dotdots_from_path_entries(&orig_path_list); + + buf = malloc((PATH_MAX + 1) * sizeof(char)); + memset(buf, '\0', PATH_MAX + 1); + + path_entries_to_string(buf, orig_path_list.pl_first); + free_path_entries(&orig_path_list); + SB_LOG(SB_LOGLEVEL_NOISE, - "sb_decolonize_path returns '%s'", buf); + "sb_path_resolution returns '%s'", buf); return buf; } +/* ========== Public interfaces to the mapping & resolution code: ========== */ + /* make sure to use disable_mapping(m); - * to prevent recursive calls to this function + * to prevent recursive calls to this function. + * Returns a pointer to an allocated buffer which contains the result. */ char *scratchbox_path3(const char *binary_name, const char *func_name, const char *path, const char *mapping_mode, - int *ro_flagp) + int *ro_flagp, + int dont_resolve_final_symlink) { char work_dir[PATH_MAX + 1]; - char *tmp = NULL, *decolon_path = NULL; - char pidlink[17]; /* /proc/2^8/exe */ struct lua_instance *luaif; - int ro_flag; + char *mapping_result; + + SB_LOG(SB_LOGLEVEL_DEBUG, "scratchbox_path3: %s(%s)", func_name, path); #ifdef EXTREME_DEBUGGING #define SIZE 100 @@ -184,6 +715,10 @@ char *scratchbox_path3(const char *binary_name, for (i = 0; i < nptrs; i++) SB_LOG(SB_LOGLEVEL_DEBUG, "%s\n", strings[i]); #endif + if (!path || !*path) { + /* an empty path shall always remain empty */ + return strdup(""); + } switch (lua_engine_state) { case LES_NOT_INITIALIZED: @@ -236,61 +771,57 @@ char *scratchbox_path3(const char *binary_name, } disable_mapping(luaif); - decolon_path = sb_decolonize_path(path); - - if (!decolon_path) { - SB_LOG(SB_LOGLEVEL_ERROR, - "ERROR: scratchbox_path3: decolon_path failed [%s]", - func_name); - return NULL; - } - - memset(work_dir, '\0', PATH_MAX+1); - snprintf(pidlink, sizeof(pidlink), "/proc/%i/exe", getpid()); - getcwd(work_dir, PATH_MAX); - - lua_getfield(luaif->lua, LUA_GLOBALSINDEX, "sbox_translate_path"); - lua_pushstring(luaif->lua, mapping_mode); - lua_pushstring(luaif->lua, binary_name); - lua_pushstring(luaif->lua, func_name); - lua_pushstring(luaif->lua, work_dir); - lua_pushstring(luaif->lua, decolon_path); - lua_call(luaif->lua, 5, 2); /* five arguments, returns path+ro_flag */ - - tmp = (char *)lua_tostring(luaif->lua, -2); - if (tmp) { - tmp = strdup(tmp); + { + /* Mapping disabled inside this block - do not use "return"!! */ + char *decolon_path = NULL; + char *full_path_for_rule_selection; + + full_path_for_rule_selection = sb_decolonize_path(path); + + /* FIXME: work_dir should be unnecessary if path is absolute? */ + memset(work_dir, '\0', sizeof(work_dir)); + getcwd(work_dir, sizeof(work_dir)-1); + + SB_LOG(SB_LOGLEVEL_DEBUG, + "scratchbox_path3: process '%s', n='%s'", + path, full_path_for_rule_selection); + decolon_path = sb_path_resolution(0, + luaif, mapping_mode, binary_name, func_name, + work_dir, path, full_path_for_rule_selection, + dont_resolve_final_symlink); + + if (!decolon_path) { + SB_LOG(SB_LOGLEVEL_ERROR, + "ERROR: scratchbox_path3: decolon_path failed [%s]", + func_name); + mapping_result = NULL; + } else { + SB_LOG(SB_LOGLEVEL_NOISE2, + "scratchbox_path3: decolon_path='%s'" + " work_dir='%s'", + decolon_path, work_dir); + + mapping_result = call_lua_function_sbox_translate_path( + SB_LOGLEVEL_INFO, + luaif, mapping_mode, binary_name, func_name, + work_dir, decolon_path, ro_flagp, + decolon_path); + } + if(decolon_path) free(decolon_path); + if(full_path_for_rule_selection) free(full_path_for_rule_selection); } - ro_flag = lua_toboolean(luaif->lua, -1); - if (ro_flagp) *ro_flagp = ro_flag; - - lua_pop(luaif->lua, 2); - enable_mapping(luaif); - if (strcmp(tmp, decolon_path) == 0) { - free(decolon_path); - free(tmp); - /* NOTE: Following SB_LOG() call is used by the log - * postprocessor script "sb2logz". Do not change - * without making a corresponding change to the script! - */ - SB_LOG(SB_LOGLEVEL_INFO, "pass: %s '%s'%s", - func_name, path, (ro_flag ? " (readonly)" : "")); - return strdup(path); - } else { - free(decolon_path); - /* NOTE: Following SB_LOG() call is used by the log - * postprocessor script "sb2logz". Do not change - * without making a corresponding change to the script! - */ - SB_LOG(SB_LOGLEVEL_INFO, "mapped: %s '%s' -> '%s'%s", - func_name, path, tmp, (ro_flag ? " (readonly)" : "")); - return tmp; - } + SB_LOG(SB_LOGLEVEL_NOISE2, "scratchbox_path3: mapping_result='%s'", + mapping_result ? mapping_result : "<No result>"); + return(mapping_result); } -char *scratchbox_path(const char *func_name, const char *path, int *ro_flagp) +char *scratchbox_path( + const char *func_name, + const char *path, + int *ro_flagp, + int dont_resolve_final_symlink) { char binary_name[PATH_MAX+1]; char *bin_name; @@ -306,6 +837,6 @@ char *scratchbox_path(const char *func_name, const char *path, int *ro_flagp) mapping_mode = "simple"; } return (scratchbox_path3(binary_name, func_name, path, mapping_mode, - ro_flagp)); + ro_flagp, dont_resolve_final_symlink)); } diff --git a/luaif/sb_log.c b/luaif/sb_log.c index bb57ad8..47e902a 100644 --- a/luaif/sb_log.c +++ b/luaif/sb_log.c @@ -123,6 +123,9 @@ void sblog_init(void) } else if (!strcmp(level_str,"noise")) { sb_loglevel__ = SB_LOGLEVEL_NOISE; sb_log_state.sbl_print_file_and_line = 1; + } else if (!strcmp(level_str,"noise2")) { + sb_loglevel__ = SB_LOGLEVEL_NOISE2; + sb_log_state.sbl_print_file_and_line = 1; } else { sb_loglevel__ = SB_LOGLEVEL_INFO; } diff --git a/modeconf/gcc-specs.maemo b/modeconf/gcc-specs.maemo new file mode 100644 index 0000000..43e9997 --- /dev/null +++ b/modeconf/gcc-specs.maemo @@ -0,0 +1,9 @@ + +*cross_compile: +0 + +%rename cpp old_cpp + +*cpp: +-isystem /usr/local/include -isystem /usr/include %(old_cpp) + diff --git a/modeconf/sb2rc.maemo b/modeconf/sb2rc.maemo index 9431037..d8ab6fe 100644 --- a/modeconf/sb2rc.maemo +++ b/modeconf/sb2rc.maemo @@ -5,6 +5,8 @@ # This is used by "sb2-check-pkg-mappings": # N.B. *bin directories needs to be ignored, because there are many # lib*-dev -packages that want to add some configurations tools to /usr/bin, etc +# N.B2: /usr/share/zoneinfo/localtime is typically a symlink to /etc/localtime +# and needs to be ignored SB2_CHECK_PKG_MAPPINGS_IGNORE_LIST=" \ /usr/bin /usr/sbin /sbin \ /etc \ @@ -17,11 +19,28 @@ SB2_CHECK_PKG_MAPPINGS_IGNORE_LIST=" \ /usr/share/info \ /usr/share/sgml \ /usr/share/perl5 \ + /usr/share/gconf \ /usr/share/base-passwd \ /usr/share/lintian/overrides/dpkg-dev \ + /usr/share/zoneinfo/localtime \ /usr/lib/dpkg " # This is for the "dpkg" wrapper: SBOX_DPKG_WRAPPER_DENY_INSTALL=yes # and a similar setting for "apt-get" wrapper: SBOX_APT_GET_WRAPPER_DENY_INSTALL=yes + +# for sb2-init: +if [ -n "$ARCH" ] +then + SB2INIT_DEB_BUILD_GNU_TYPE=$ARCH-linux-gnueabi + SB2INIT_DEB_HOST_GNU_TYPE=$ARCH-linux-gnueabi + SB2INIT_CROSS_GCC_PREFIX_LIST=$ARCH-linux-:$ARCH-linux-gnueabi- + SB2INIT_DEB_BUILD_GNU_SYSTEM="linux-gnueabi" + SB2INIT_DEB_HOST_GNU_SYSTEM="linux-gnueabi" + + SB2INIT_DEB_HOST_ARCH_CPU=$ARCH + SB2INIT_DEB_BUILD_ARCH_CPU=$ARCH + SB2INIT_DEB_BUILD_ARCH_ABI="gnueabi" +fi + diff --git a/preload/Makefile b/preload/Makefile index 74e0c30..fd0a3af 100644 --- a/preload/Makefile +++ b/preload/Makefile @@ -1,4 +1,4 @@ -objs := wrappers.o libsb2.o sb_exec.o +objs := wrappers.o libsb2.o sb_exec.o glob.o glob64.o ifeq ($(shell uname -s),Linux) LIBSB2_LDFLAGS = -Wl,-soname=$(LIBSB2_SONAME) \ diff --git a/preload/gen-interface.pl b/preload/gen-interface.pl index 07647b7..d0bd1a6 100755 --- a/preload/gen-interface.pl +++ b/preload/gen-interface.pl @@ -54,6 +54,12 @@ # provided check). "varname" must be the same name which was specified # to map() or map_at(). "error_code" will be assigned to errno, and # the failure will always be logged (SB_LOG_NOTICE level) +# - "dont_resolve_final_symlink" is used to prefix "map" modifiers where the +# final symbolic link should not be followed, because the call operates +# on the symlink itself (for example, see the difference between stat() and +# lstat()). NOTE: THIS MUST BE USED BEFORE THE map() OR map_at() MODIFIERS! +# - "resolve_final_symlink" is the opposite of "dont_resolve_final_symlink" +# (and it is on by default) # # For "WRAP" only: # - "create_nomap_nolog_version" creates a direct interface function to the @@ -428,6 +434,7 @@ sub process_wrap_or_gate_modifiers { 'va_list_handler_code' => "", 'va_list_end_code' => "", 'mapped_params_by_orig_name' => {}, + 'dont_resolve_final_symlink' => 0, # processing modifiers may change the parameter list # (but always we'll start with a copy of the original names) @@ -453,6 +460,7 @@ sub process_wrap_or_gate_modifiers { my $new_name = "mapped__".$param_to_be_mapped; my $ro_flag = $param_to_be_mapped."_is_readonly"; + my $no_symlink_resolve = $mods->{'dont_resolve_final_symlink'}; $mods->{'mapped_params_by_orig_name'}->{$param_to_be_mapped} = $new_name; $mods->{'path_mapping_vars'} .= @@ -460,7 +468,8 @@ sub process_wrap_or_gate_modifiers { "\tint $ro_flag = 0;\n"; $mods->{'path_mapping_code'} .= "\tSBOX_MAP_PATH($param_to_be_mapped, ". - "$new_name, &$ro_flag);\n"; + "$new_name, &$ro_flag, ". + "$no_symlink_resolve);\n"; $mods->{'free_path_mapping_vars_code'} .= "\tif($new_name) free($new_name);\n"; @@ -473,6 +482,7 @@ sub process_wrap_or_gate_modifiers { my $new_name = "mapped__".$param_to_be_mapped; my $ro_flag = $param_to_be_mapped."_is_readonly"; + my $no_symlink_resolve = $mods->{'dont_resolve_final_symlink'}; $mods->{'mapped_params_by_orig_name'}->{$param_to_be_mapped} = $new_name; $mods->{'path_mapping_vars'} .= @@ -480,7 +490,8 @@ sub process_wrap_or_gate_modifiers { "\tint $ro_flag = 0;\n"; $mods->{'path_mapping_code'} .= "\tSBOX_MAP_PATH_AT($fd_param, ". - "$param_to_be_mapped, $new_name, &$ro_flag);\n"; + "$param_to_be_mapped, $new_name, &$ro_flag, ". + "$no_symlink_resolve);\n"; $mods->{'free_path_mapping_vars_code'} .= "\tif($new_name) free($new_name);\n"; @@ -521,6 +532,8 @@ sub process_wrap_or_gate_modifiers { create_code_for_va_list_get_mode( "", $fn->{'last_named_var'}); $varargs_handled = 1; + } elsif($modifiers[$i] eq 'dont_resolve_final_symlink') { + $mods->{'dont_resolve_final_symlink'} = 1; } elsif(($modifiers[$i] =~ m/^optional_arg_is_create_mode\((.*)\)$/) && ($fn->{'has_varargs'})) { my $va_list_condition = $1; diff --git a/preload/glob.c b/preload/glob.c new file mode 100644 index 0000000..b43975b --- /dev/null +++ b/preload/glob.c @@ -0,0 +1,1557 @@ +/* Copied from glibc-2.7 to Scratchbox 2, with minimal modifications + * (search for "SB2" in the code below) + * + * added from glibc's posix/glob.c: +*/ + +/* Copyright (C) 1991-2002, 2003, 2004, 2005, 2006, 2007 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <glob.h> + +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stddef.h> + +/* Outcomment the following line for production quality code. */ +/* #define NDEBUG 1 */ +#include <assert.h> + +#include <stdio.h> /* Needed on stupid SunOS for assert. */ + +#if !defined _LIBC || !defined GLOB_ONLY_P +#if defined HAVE_UNISTD_H || defined _LIBC +# include <unistd.h> +# ifndef POSIX +# ifdef _POSIX_VERSION +# define POSIX +# endif +# endif +#endif + +#include <pwd.h> + +#include <errno.h> +#ifndef __set_errno +# define __set_errno(val) errno = (val) +#endif + +#if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__ +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# ifdef HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# ifdef HAVE_NDIR_H +# include <ndir.h> +# endif +# ifdef HAVE_VMSDIR_H +# include "vmsdir.h" +# endif /* HAVE_VMSDIR_H */ +#endif + + +/* In GNU systems, <dirent.h> defines this macro for us. */ +#ifdef _D_NAMLEN +# undef NAMLEN +# define NAMLEN(d) _D_NAMLEN(d) +#endif + +/* When used in the GNU libc the symbol _DIRENT_HAVE_D_TYPE is available + if the `d_type' member for `struct dirent' is available. + HAVE_STRUCT_DIRENT_D_TYPE plays the same role in GNULIB. */ +#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE +/* True if the directory entry D must be of type T. */ +# define DIRENT_MUST_BE(d, t) ((d)->d_type == (t)) + +/* True if the directory entry D might be a symbolic link. */ +# define DIRENT_MIGHT_BE_SYMLINK(d) \ + ((d)->d_type == DT_UNKNOWN || (d)->d_type == DT_LNK) + +/* True if the directory entry D might be a directory. */ +# define DIRENT_MIGHT_BE_DIR(d) \ + ((d)->d_type == DT_DIR || DIRENT_MIGHT_BE_SYMLINK (d)) + +#else /* !HAVE_D_TYPE */ +# define DIRENT_MUST_BE(d, t) false +# define DIRENT_MIGHT_BE_SYMLINK(d) true +# define DIRENT_MIGHT_BE_DIR(d) true +#endif /* HAVE_D_TYPE */ + +/* If the system has the `struct dirent64' type we use it internally. */ +#if defined _LIBC && !defined COMPILE_GLOB64 +# if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__ +# define CONVERT_D_NAMLEN(d64, d32) +# else +# define CONVERT_D_NAMLEN(d64, d32) \ + (d64)->d_namlen = (d32)->d_namlen; +# endif + +# if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ +# define CONVERT_D_INO(d64, d32) +# else +# define CONVERT_D_INO(d64, d32) \ + (d64)->d_ino = (d32)->d_ino; +# endif + +# ifdef _DIRENT_HAVE_D_TYPE +# define CONVERT_D_TYPE(d64, d32) \ + (d64)->d_type = (d32)->d_type; +# else +# define CONVERT_D_TYPE(d64, d32) +# endif + +# define CONVERT_DIRENT_DIRENT64(d64, d32) \ + memcpy ((d64)->d_name, (d32)->d_name, NAMLEN (d32) + 1); \ + CONVERT_D_NAMLEN (d64, d32) \ + CONVERT_D_INO (d64, d32) \ + CONVERT_D_TYPE (d64, d32) +#endif + + +#if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ +/* Posix does not require that the d_ino field be present, and some + systems do not provide it. */ +# define REAL_DIR_ENTRY(dp) 1 +#else +# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0) +#endif /* POSIX */ + +#include <stdlib.h> +#include <string.h> + +/* NAME_MAX is usually defined in <dirent.h> or <limits.h>. */ +#include <limits.h> +#ifndef NAME_MAX +# define NAME_MAX (sizeof (((struct dirent *) 0)->d_name)) +#endif + +#include <alloca.h> + +#ifdef _LIBC +# undef strdup +# define strdup(str) __strdup (str) +# define sysconf(id) __sysconf (id) +# define closedir(dir) __closedir (dir) +# define opendir(name) __opendir (name) +# define readdir(str) __readdir64 (str) +# define getpwnam_r(name, bufp, buf, len, res) \ + __getpwnam_r (name, bufp, buf, len, res) +# ifndef __stat64 +# define __stat64(fname, buf) __xstat64 (_STAT_VER, fname, buf) +# endif +# define struct_stat64 struct stat64 +#else /* !_LIBC */ +#if 0 /* SB2: use unistd.c */ +# include "getlogin_r.h" +# include "mempcpy.h" +# include "stat-macros.h" +# include "strdup.h" +#else +# include <unistd.h> +# define __libc_use_alloca(allocasize) (0) +#endif +# define __stat64(fname, buf) stat (fname, buf) +# define struct_stat64 struct stat +#ifndef __stat /* SB2 */ +# define __stat(fname, buf) stat (fname, buf) +#endif +# define __alloca alloca +#ifndef __readdir /* SB2 */ +# define __readdir readdir +#endif +# define __readdir64 readdir64 +# define __glob_pattern_p glob_pattern_p +#endif /* _LIBC */ + +#include <fnmatch.h> + +#ifdef _SC_GETPW_R_SIZE_MAX +# define GETPW_R_SIZE_MAX() sysconf (_SC_GETPW_R_SIZE_MAX) +#else +# define GETPW_R_SIZE_MAX() (-1) +#endif +#ifdef _SC_LOGIN_NAME_MAX +# define GET_LOGIN_NAME_MAX() sysconf (_SC_LOGIN_NAME_MAX) +#else +# define GET_LOGIN_NAME_MAX() (-1) +#endif + +static const char *next_brace_sub (const char *begin, int flags) __THROW; + +#endif /* !defined _LIBC || !defined GLOB_ONLY_P */ + +#ifndef attribute_hidden +# define attribute_hidden +#endif + +static int glob_in_dir (const char *pattern, const char *directory, + int flags, int (*errfunc) (const char *, int), + glob_t *pglob); +extern int __glob_pattern_type (const char *pattern, int quote) + attribute_hidden; + +#if !defined _LIBC || !defined GLOB_ONLY_P +static int prefix_array (const char *prefix, char **array, size_t n) __THROW; +static int collated_compare (const void *, const void *) __THROW; + + +/* Find the end of the sub-pattern in a brace expression. */ +static const char * +next_brace_sub (const char *cp, int flags) +{ + unsigned int depth = 0; + while (*cp != '\0') + if ((flags & GLOB_NOESCAPE) == 0 && *cp == '\\') + { + if (*++cp == '\0') + break; + ++cp; + } + else + { + if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0)) + break; + + if (*cp++ == '{') + depth++; + } + + return *cp != '\0' ? cp : NULL; +} + +#endif /* !defined _LIBC || !defined GLOB_ONLY_P */ + +/* Do glob searching for PATTERN, placing results in PGLOB. + The bits defined above may be set in FLAGS. + If a directory cannot be opened or read and ERRFUNC is not nil, + it is called with the pathname that caused the error, and the + `errno' value from the failing call; if it returns non-zero + `glob' returns GLOB_ABORTED; if it returns zero, the error is ignored. + If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned. + Otherwise, `glob' returns zero. */ +#if 1 /* SB2 */ +/* this is called "do_glob" / "do_glob64" in SB2, + * "glob" & "glob64" are the wrappers */ +# ifdef COMPILE_GLOB64 +# define glob do_glob64 +# else +# define glob do_glob +# endif +#endif +int +#ifdef GLOB_ATTRIBUTE +GLOB_ATTRIBUTE +#endif +glob (pattern, flags, errfunc, pglob) + const char *pattern; + int flags; + int (*errfunc) (const char *, int); + glob_t *pglob; +{ + const char *filename; + const char *dirname; + size_t dirlen; + int status; + size_t oldcount; + int meta; + int dirname_modified; + glob_t dirs; + + if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) + { + __set_errno (EINVAL); + return -1; + } + + if (!(flags & GLOB_DOOFFS)) + /* Have to do this so `globfree' knows where to start freeing. It + also makes all the code that uses gl_offs simpler. */ + pglob->gl_offs = 0; + + if (flags & GLOB_BRACE) + { + const char *begin; + + if (flags & GLOB_NOESCAPE) + begin = strchr (pattern, '{'); + else + { + begin = pattern; + while (1) + { + if (*begin == '\0') + { + begin = NULL; + break; + } + + if (*begin == '\\' && begin[1] != '\0') + ++begin; + else if (*begin == '{') + break; + + ++begin; + } + } + + if (begin != NULL) + { + /* Allocate working buffer large enough for our work. Note that + we have at least an opening and closing brace. */ + size_t firstc; + char *alt_start; + const char *p; + const char *next; + const char *rest; + size_t rest_len; +#ifdef __GNUC__ + char onealt[strlen (pattern) - 1]; +#else + char *onealt = (char *) malloc (strlen (pattern) - 1); + if (onealt == NULL) + { + if (!(flags & GLOB_APPEND)) + { + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + } + return GLOB_NOSPACE; + } +#endif + + /* We know the prefix for all sub-patterns. */ + alt_start = mempcpy (onealt, pattern, begin - pattern); + + /* Find the first sub-pattern and at the same time find the + rest after the closing brace. */ + next = next_brace_sub (begin + 1, flags); + if (next == NULL) + { + /* It is an illegal expression. */ +#ifndef __GNUC__ + free (onealt); +#endif + return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob); + } + + /* Now find the end of the whole brace expression. */ + rest = next; + while (*rest != '}') + { + rest = next_brace_sub (rest + 1, flags); + if (rest == NULL) + { + /* It is an illegal expression. */ +#ifndef __GNUC__ + free (onealt); +#endif + return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob); + } + } + /* Please note that we now can be sure the brace expression + is well-formed. */ + rest_len = strlen (++rest) + 1; + + /* We have a brace expression. BEGIN points to the opening {, + NEXT points past the terminator of the first element, and END + points past the final }. We will accumulate result names from + recursive runs for each brace alternative in the buffer using + GLOB_APPEND. */ + + if (!(flags & GLOB_APPEND)) + { + /* This call is to set a new vector, so clear out the + vector so we can append to it. */ + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + } + firstc = pglob->gl_pathc; + + p = begin + 1; + while (1) + { + int result; + + /* Construct the new glob expression. */ + mempcpy (mempcpy (alt_start, p, next - p), rest, rest_len); + + result = glob (onealt, + ((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC)) + | GLOB_APPEND), errfunc, pglob); + + /* If we got an error, return it. */ + if (result && result != GLOB_NOMATCH) + { +#ifndef __GNUC__ + free (onealt); +#endif + if (!(flags & GLOB_APPEND)) + { + globfree (pglob); + pglob->gl_pathc = 0; + } + return result; + } + + if (*next == '}') + /* We saw the last entry. */ + break; + + p = next + 1; + next = next_brace_sub (p, flags); + assert (next != NULL); + } + +#ifndef __GNUC__ + free (onealt); +#endif + + if (pglob->gl_pathc != firstc) + /* We found some entries. */ + return 0; + else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC))) + return GLOB_NOMATCH; + } + } + + /* Find the filename. */ + filename = strrchr (pattern, '/'); +#if defined __MSDOS__ || defined WINDOWS32 + /* The case of "d:pattern". Since `:' is not allowed in + file names, we can safely assume that wherever it + happens in pattern, it signals the filename part. This + is so we could some day support patterns like "[a-z]:foo". */ + if (filename == NULL) + filename = strchr (pattern, ':'); +#endif /* __MSDOS__ || WINDOWS32 */ + dirname_modified = 0; + if (filename == NULL) + { + /* This can mean two things: a simple name or "~name". The latter + case is nothing but a notation for a directory. */ + if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && pattern[0] == '~') + { + dirname = pattern; + dirlen = strlen (pattern); + + /* Set FILENAME to NULL as a special flag. This is ugly but + other solutions would require much more code. We test for + this special case below. */ + filename = NULL; + } + else + { + filename = pattern; +#ifdef _AMIGA + dirname = ""; +#else + dirname = "."; +#endif + dirlen = 0; + } + } + else if (filename == pattern + || (filename == pattern + 1 && pattern[0] == '\\' + && (flags & GLOB_NOESCAPE) == 0)) + { + /* "/pattern" or "\\/pattern". */ + dirname = "/"; + dirlen = 1; + ++filename; + } + else + { + char *newp; + dirlen = filename - pattern; +#if defined __MSDOS__ || defined WINDOWS32 + if (*filename == ':' + || (filename > pattern + 1 && filename[-1] == ':')) + { + char *drive_spec; + + ++dirlen; + drive_spec = (char *) __alloca (dirlen + 1); + *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0'; + /* For now, disallow wildcards in the drive spec, to + prevent infinite recursion in glob. */ + if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE))) + return GLOB_NOMATCH; + /* If this is "d:pattern", we need to copy `:' to DIRNAME + as well. If it's "d:/pattern", don't remove the slash + from "d:/", since "d:" and "d:/" are not the same.*/ + } +#endif + newp = (char *) __alloca (dirlen + 1); + *((char *) mempcpy (newp, pattern, dirlen)) = '\0'; + dirname = newp; + ++filename; + + if (filename[0] == '\0' +#if defined __MSDOS__ || defined WINDOWS32 + && dirname[dirlen - 1] != ':' + && (dirlen < 3 || dirname[dirlen - 2] != ':' + || dirname[dirlen - 1] != '/') +#endif + && dirlen > 1) + /* "pattern/". Expand "pattern", appending slashes. */ + { + int orig_flags = flags; + if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\') + { + /* "pattern\\/". Remove the final backslash if it hasn't + been quoted. */ + char *p = (char *) &dirname[dirlen - 1]; + + while (p > dirname && p[-1] == '\\') --p; + if ((&dirname[dirlen] - p) & 1) + { + *(char *) &dirname[--dirlen] = '\0'; + flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC); + } + } + int val = glob (dirname, flags | GLOB_MARK, errfunc, pglob); + if (val == 0) + pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK) + | (flags & GLOB_MARK)); + else if (val == GLOB_NOMATCH && flags != orig_flags) + { + /* Make sure globfree (&dirs); is a nop. */ + dirs.gl_pathv = NULL; + flags = orig_flags; + oldcount = pglob->gl_pathc + pglob->gl_offs; + goto no_matches; + } + return val; + } + } + + if (!(flags & GLOB_APPEND)) + { + pglob->gl_pathc = 0; + if (!(flags & GLOB_DOOFFS)) + pglob->gl_pathv = NULL; + else + { + size_t i; + pglob->gl_pathv = (char **) malloc ((pglob->gl_offs + 1) + * sizeof (char *)); + if (pglob->gl_pathv == NULL) + return GLOB_NOSPACE; + + for (i = 0; i <= pglob->gl_offs; ++i) + pglob->gl_pathv[i] = NULL; + } + } + + oldcount = pglob->gl_pathc + pglob->gl_offs; + +#ifndef VMS + if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~') + { + if (dirname[1] == '\0' || dirname[1] == '/' + || (!(flags & GLOB_NOESCAPE) && dirname[1] == '\\' + && (dirname[2] == '\0' || dirname[2] == '/'))) + { + /* Look up home directory. */ + const char *home_dir = getenv ("HOME"); +# ifdef _AMIGA + if (home_dir == NULL || home_dir[0] == '\0') + home_dir = "SYS:"; +# else +# ifdef WINDOWS32 + if (home_dir == NULL || home_dir[0] == '\0') + home_dir = "c:/users/default"; /* poor default */ +# else + if (home_dir == NULL || home_dir[0] == '\0') + { + int success; + char *name; + size_t buflen = GET_LOGIN_NAME_MAX () + 1; + + if (buflen == 0) + /* `sysconf' does not support _SC_LOGIN_NAME_MAX. Try + a moderate value. */ + buflen = 20; + name = (char *) __alloca (buflen); + + success = getlogin_r (name, buflen) == 0; + if (success) + { + struct passwd *p; +# if defined HAVE_GETPWNAM_R || defined _LIBC + long int pwbuflen = GETPW_R_SIZE_MAX (); + char *pwtmpbuf; + struct passwd pwbuf; + int save = errno; + +# ifndef _LIBC + if (pwbuflen == -1) + /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX. + Try a moderate value. */ + pwbuflen = 1024; +# endif + pwtmpbuf = (char *) __alloca (pwbuflen); + + while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p) + != 0) + { + if (errno != ERANGE) + { + p = NULL; + break; + } +# ifdef _LIBC + pwtmpbuf = extend_alloca (pwtmpbuf, pwbuflen, + 2 * pwbuflen); +# else + pwbuflen *= 2; + pwtmpbuf = (char *) __alloca (pwbuflen); +# endif + __set_errno (save); + } +# else + p = getpwnam (name); +# endif + if (p != NULL) + home_dir = p->pw_dir; + } + } + if (home_dir == NULL || home_dir[0] == '\0') + { + if (flags & GLOB_TILDE_CHECK) + return GLOB_NOMATCH; + else + home_dir = "~"; /* No luck. */ + } +# endif /* WINDOWS32 */ +# endif + /* Now construct the full directory. */ + if (dirname[1] == '\0') + { + dirname = home_dir; + dirlen = strlen (dirname); + } + else + { + char *newp; + size_t home_len = strlen (home_dir); + newp = (char *) __alloca (home_len + dirlen); + mempcpy (mempcpy (newp, home_dir, home_len), + &dirname[1], dirlen); + dirname = newp; + dirlen += home_len - 1; + } + dirname_modified = 1; + } +# if !defined _AMIGA && !defined WINDOWS32 + else + { + char *end_name = strchr (dirname, '/'); + const char *user_name; + const char *home_dir; + char *unescape = NULL; + + if (!(flags & GLOB_NOESCAPE)) + { + if (end_name == NULL) + { + unescape = strchr (dirname, '\\'); + if (unescape) + end_name = strchr (unescape, '\0'); + } + else + unescape = memchr (dirname, '\\', end_name - dirname); + } + if (end_name == NULL) + user_name = dirname + 1; + else + { + char *newp; + newp = (char *) __alloca (end_name - dirname); + if (unescape != NULL) + { + char *p = mempcpy (newp, dirname + 1, + unescape - dirname - 1); + char *q = unescape; + while (*q != '\0') + { + if (*q == '\\') + { + if (q[1] == '\0') + { + /* "~fo\\o\\" unescape to user_name "foo\\", + but "~fo\\o\\/" unescape to user_name + "foo". */ + if (filename == NULL) + *p++ = '\\'; + break; + } + ++q; + } + *p++ = *q++; + } + *p = '\0'; + } + else + *((char *) mempcpy (newp, dirname + 1, end_name - dirname)) + = '\0'; + user_name = newp; + } + + /* Look up specific user's home directory. */ + { + struct passwd *p; +# if defined HAVE_GETPWNAM_R || defined _LIBC + long int buflen = GETPW_R_SIZE_MAX (); + char *pwtmpbuf; + struct passwd pwbuf; + int save = errno; + +# ifndef _LIBC + if (buflen == -1) + /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX. Try a + moderate value. */ + buflen = 1024; +# endif + pwtmpbuf = (char *) __alloca (buflen); + + while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0) + { + if (errno != ERANGE) + { + p = NULL; + break; + } +# ifdef _LIBC + pwtmpbuf = extend_alloca (pwtmpbuf, buflen, 2 * buflen); +# else + buflen *= 2; + pwtmpbuf = __alloca (buflen); +# endif + __set_errno (save); + } +# else + p = getpwnam (user_name); +# endif + if (p != NULL) + home_dir = p->pw_dir; + else + home_dir = NULL; + } + /* If we found a home directory use this. */ + if (home_dir != NULL) + { + char *newp; + size_t home_len = strlen (home_dir); + size_t rest_len = end_name == NULL ? 0 : strlen (end_name); + newp = (char *) __alloca (home_len + rest_len + 1); + *((char *) mempcpy (mempcpy (newp, home_dir, home_len), + end_name, rest_len)) = '\0'; + dirname = newp; + dirlen = home_len + rest_len; + dirname_modified = 1; + } + else + if (flags & GLOB_TILDE_CHECK) + /* We have to regard it as an error if we cannot find the + home directory. */ + return GLOB_NOMATCH; + } +# endif /* Not Amiga && not WINDOWS32. */ + } +#endif /* Not VMS. */ + + /* Now test whether we looked for "~" or "~NAME". In this case we + can give the answer now. */ + if (filename == NULL) + { + struct stat st; + struct_stat64 st64; + + /* Return the directory if we don't check for error or if it exists. */ + if ((flags & GLOB_NOCHECK) + || (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) + ? ((*pglob->gl_stat) (dirname, &st) == 0 + && S_ISDIR (st.st_mode)) + : (__stat64 (dirname, &st64) == 0 && S_ISDIR (st64.st_mode))))) + { + int newcount = pglob->gl_pathc + pglob->gl_offs; + char **new_gl_pathv; + + new_gl_pathv + = (char **) realloc (pglob->gl_pathv, + (newcount + 1 + 1) * sizeof (char *)); + if (new_gl_pathv == NULL) + { + nospace: + free (pglob->gl_pathv); + pglob->gl_pathv = NULL; + pglob->gl_pathc = 0; + return GLOB_NOSPACE; + } + pglob->gl_pathv = new_gl_pathv; + + if (flags & GLOB_MARK) + { + char *p; + pglob->gl_pathv[newcount] = malloc (dirlen + 2); + if (pglob->gl_pathv[newcount] == NULL) + goto nospace; + p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen); + p[0] = '/'; + p[1] = '\0'; + } + else + { + pglob->gl_pathv[newcount] = strdup (dirname); + if (pglob->gl_pathv[newcount] == NULL) + goto nospace; + } + pglob->gl_pathv[++newcount] = NULL; + ++pglob->gl_pathc; + pglob->gl_flags = flags; + + return 0; + } + + /* Not found. */ + return GLOB_NOMATCH; + } + + meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE)); + /* meta is 1 if correct glob pattern containing metacharacters. + If meta has bit (1 << 2) set, it means there was an unterminated + [ which we handle the same, using fnmatch. Broken unterminated + pattern bracket expressions ought to be rare enough that it is + not worth special casing them, fnmatch will do the right thing. */ + if (meta & 5) + { + /* The directory name contains metacharacters, so we + have to glob for the directory, and then glob for + the pattern in each directory found. */ + size_t i; + + if (!(flags & GLOB_NOESCAPE) && dirlen > 0 && dirname[dirlen - 1] == '\\') + { + /* "foo\\/bar". Remove the final backslash from dirname + if it has not been quoted. */ + char *p = (char *) &dirname[dirlen - 1]; + + while (p > dirname && p[-1] == '\\') --p; + if ((&dirname[dirlen] - p) & 1) + *(char *) &dirname[--dirlen] = '\0'; + } + + if (__builtin_expect ((flags & GLOB_ALTDIRFUNC) != 0, 0)) + { + /* Use the alternative access functions also in the recursive + call. */ + dirs.gl_opendir = pglob->gl_opendir; + dirs.gl_readdir = pglob->gl_readdir; + dirs.gl_closedir = pglob->gl_closedir; + dirs.gl_stat = pglob->gl_stat; + dirs.gl_lstat = pglob->gl_lstat; + } + + status = glob (dirname, + ((flags & (GLOB_ERR | GLOB_NOESCAPE + | GLOB_ALTDIRFUNC)) + | GLOB_NOSORT | GLOB_ONLYDIR), + errfunc, &dirs); + if (status != 0) + { + if ((flags & GLOB_NOCHECK) == 0 || status != GLOB_NOMATCH) + return status; + goto no_matches; + } + + /* We have successfully globbed the preceding directory name. + For each name we found, call glob_in_dir on it and FILENAME, + appending the results to PGLOB. */ + for (i = 0; i < dirs.gl_pathc; ++i) + { + int old_pathc; + +#ifdef SHELL + { + /* Make globbing interruptible in the bash shell. */ + extern int interrupt_state; + + if (interrupt_state) + { + globfree (&dirs); + return GLOB_ABORTED; + } + } +#endif /* SHELL. */ + + old_pathc = pglob->gl_pathc; + status = glob_in_dir (filename, dirs.gl_pathv[i], + ((flags | GLOB_APPEND) + & ~(GLOB_NOCHECK | GLOB_NOMAGIC)), + errfunc, pglob); + if (status == GLOB_NOMATCH) + /* No matches in this directory. Try the next. */ + continue; + + if (status != 0) + { + globfree (&dirs); + globfree (pglob); + pglob->gl_pathc = 0; + return status; + } + + /* Stick the directory on the front of each name. */ + if (prefix_array (dirs.gl_pathv[i], + &pglob->gl_pathv[old_pathc + pglob->gl_offs], + pglob->gl_pathc - old_pathc)) + { + globfree (&dirs); + globfree (pglob); + pglob->gl_pathc = 0; + return GLOB_NOSPACE; + } + } + + flags |= GLOB_MAGCHAR; + + /* We have ignored the GLOB_NOCHECK flag in the `glob_in_dir' calls. + But if we have not found any matching entry and the GLOB_NOCHECK + flag was set we must return the input pattern itself. */ + if (pglob->gl_pathc + pglob->gl_offs == oldcount) + { + no_matches: + /* No matches. */ + if (flags & GLOB_NOCHECK) + { + int newcount = pglob->gl_pathc + pglob->gl_offs; + char **new_gl_pathv; + + new_gl_pathv = (char **) realloc (pglob->gl_pathv, + (newcount + 2) + * sizeof (char *)); + if (new_gl_pathv == NULL) + { + globfree (&dirs); + return GLOB_NOSPACE; + } + pglob->gl_pathv = new_gl_pathv; + + pglob->gl_pathv[newcount] = __strdup (pattern); + if (pglob->gl_pathv[newcount] == NULL) + { + globfree (&dirs); + globfree (pglob); + pglob->gl_pathc = 0; + return GLOB_NOSPACE; + } + + ++pglob->gl_pathc; + ++newcount; + + pglob->gl_pathv[newcount] = NULL; + pglob->gl_flags = flags; + } + else + { + globfree (&dirs); + return GLOB_NOMATCH; + } + } + + globfree (&dirs); + } + else + { + int old_pathc = pglob->gl_pathc; + int orig_flags = flags; + + if (meta & 2) + { + char *p = strchr (dirname, '\\'), *q; + /* We need to unescape the dirname string. It is certainly + allocated by alloca, as otherwise filename would be NULL + or dirname wouldn't contain backslashes. */ + q = p; + do + { + if (*p == '\\') + { + *q = *++p; + --dirlen; + } + else + *q = *p; + ++q; + } + while (*p++ != '\0'); + dirname_modified = 1; + } + if (dirname_modified) + flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC); + status = glob_in_dir (filename, dirname, flags, errfunc, pglob); + if (status != 0) + { + if (status == GLOB_NOMATCH && flags != orig_flags + && pglob->gl_pathc + pglob->gl_offs == oldcount) + { + /* Make sure globfree (&dirs); is a nop. */ + dirs.gl_pathv = NULL; + flags = orig_flags; + goto no_matches; + } + return status; + } + + if (dirlen > 0) + { + /* Stick the directory on the front of each name. */ + if (prefix_array (dirname, + &pglob->gl_pathv[old_pathc + pglob->gl_offs], + pglob->gl_pathc - old_pathc)) + { + globfree (pglob); + pglob->gl_pathc = 0; + return GLOB_NOSPACE; + } + } + } + + if (flags & GLOB_MARK) + { + /* Append slashes to directory names. */ + size_t i; + struct stat st; + struct_stat64 st64; + + for (i = oldcount; i < pglob->gl_pathc + pglob->gl_offs; ++i) + if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? ((*pglob->gl_stat) (pglob->gl_pathv[i], &st) == 0 + && S_ISDIR (st.st_mode)) + : (__stat64 (pglob->gl_pathv[i], &st64) == 0 + && S_ISDIR (st64.st_mode)))) + { + size_t len = strlen (pglob->gl_pathv[i]) + 2; + char *new = realloc (pglob->gl_pathv[i], len); + if (new == NULL) + { + globfree (pglob); + pglob->gl_pathc = 0; + return GLOB_NOSPACE; + } + strcpy (&new[len - 2], "/"); + pglob->gl_pathv[i] = new; + } + } + + if (!(flags & GLOB_NOSORT)) + { + /* Sort the vector. */ + qsort (&pglob->gl_pathv[oldcount], + pglob->gl_pathc + pglob->gl_offs - oldcount, + sizeof (char *), collated_compare); + } + + return 0; +} +#if defined _LIBC && !defined glob +libc_hidden_def (glob) +#endif + + +#if !defined _LIBC || !defined GLOB_ONLY_P + +/* Free storage allocated in PGLOB by a previous `glob' call. */ +void +globfree (pglob) + register glob_t *pglob; +{ + if (pglob->gl_pathv != NULL) + { + size_t i; + for (i = 0; i < pglob->gl_pathc; ++i) + if (pglob->gl_pathv[pglob->gl_offs + i] != NULL) + free (pglob->gl_pathv[pglob->gl_offs + i]); + free (pglob->gl_pathv); + pglob->gl_pathv = NULL; + } +} +#if defined _LIBC && !defined globfree +libc_hidden_def (globfree) +#endif + + +/* Do a collated comparison of A and B. */ +static int +collated_compare (const void *a, const void *b) +{ + const char *const s1 = *(const char *const * const) a; + const char *const s2 = *(const char *const * const) b; + + if (s1 == s2) + return 0; + if (s1 == NULL) + return 1; + if (s2 == NULL) + return -1; + return strcoll (s1, s2); +} + + +/* Prepend DIRNAME to each of N members of ARRAY, replacing ARRAY's + elements in place. Return nonzero if out of memory, zero if successful. + A slash is inserted between DIRNAME and each elt of ARRAY, + unless DIRNAME is just "/". Each old element of ARRAY is freed. */ +static int +prefix_array (const char *dirname, char **array, size_t n) +{ + register size_t i; + size_t dirlen = strlen (dirname); +#if defined __MSDOS__ || defined WINDOWS32 + int sep_char = '/'; +# define DIRSEP_CHAR sep_char +#else +# define DIRSEP_CHAR '/' +#endif + + if (dirlen == 1 && dirname[0] == '/') + /* DIRNAME is just "/", so normal prepending would get us "//foo". + We want "/foo" instead, so don't prepend any chars from DIRNAME. */ + dirlen = 0; +#if defined __MSDOS__ || defined WINDOWS32 + else if (dirlen > 1) + { + if (dirname[dirlen - 1] == '/' && dirname[dirlen - 2] == ':') + /* DIRNAME is "d:/". Don't prepend the slash from DIRNAME. */ + --dirlen; + else if (dirname[dirlen - 1] == ':') + { + /* DIRNAME is "d:". Use `:' instead of `/'. */ + --dirlen; + sep_char = ':'; + } + } +#endif + + for (i = 0; i < n; ++i) + { + size_t eltlen = strlen (array[i]) + 1; + char *new = (char *) malloc (dirlen + 1 + eltlen); + if (new == NULL) + { + while (i > 0) + free (array[--i]); + return 1; + } + + { + char *endp = mempcpy (new, dirname, dirlen); + *endp++ = DIRSEP_CHAR; + mempcpy (endp, array[i], eltlen); + } + free (array[i]); + array[i] = new; + } + + return 0; +} + + +/* We must not compile this function twice. */ +#if !defined NO_GLOB_PATTERN_P /* SB2 */ +#if !defined _LIBC || !defined NO_GLOB_PATTERN_P +int +__glob_pattern_type (pattern, quote) + const char *pattern; + int quote; +{ + register const char *p; + int ret = 0; + + for (p = pattern; *p != '\0'; ++p) + switch (*p) + { + case '?': + case '*': + return 1; + + case '\\': + if (quote) + { + if (p[1] != '\0') + ++p; + ret |= 2; + } + break; + + case '[': + ret |= 4; + break; + + case ']': + if (ret & 4) + return 1; + break; + } + + return ret; +} + +/* Return nonzero if PATTERN contains any metacharacters. + Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ +int +__glob_pattern_p (pattern, quote) + const char *pattern; + int quote; +{ + return __glob_pattern_type (pattern, quote) == 1; +} +# ifdef _LIBC +weak_alias (__glob_pattern_p, glob_pattern_p) +# endif +#endif +#endif + +#endif /* !GLOB_ONLY_P */ + + +/* We put this in a separate function mainly to allow the memory + allocated with alloca to be recycled. */ +#if !defined _LIBC || !defined GLOB_ONLY_P +static int +__attribute_noinline__ +link_exists2_p (const char *dir, size_t dirlen, const char *fname, + glob_t *pglob +# ifndef _LIBC + , int flags +# endif + ) +{ + size_t fnamelen = strlen (fname); + char *fullname = (char *) __alloca (dirlen + 1 + fnamelen + 1); + struct stat st; +# ifndef _LIBC + struct_stat64 st64; +# endif + + mempcpy (mempcpy (mempcpy (fullname, dir, dirlen), "/", 1), + fname, fnamelen + 1); + +# ifdef _LIBC + return (*pglob->gl_stat) (fullname, &st) == 0; +# else + return ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? (*pglob->gl_stat) (fullname, &st) + : __stat64 (fullname, &st64)) == 0); +# endif +} +# ifdef _LIBC +# define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \ + (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) \ + ? link_exists2_p (dirname, dirnamelen, fname, pglob) \ + : ({ struct stat64 st64; \ + __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0) == 0; })) +# else +# define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \ + link_exists2_p (dirname, dirnamelen, fname, pglob, flags) +# endif +#endif + + +/* Like `glob', but PATTERN is a final pathname component, + and matches are searched for in DIRECTORY. + The GLOB_NOSORT bit in FLAGS is ignored. No sorting is ever done. + The GLOB_APPEND flag is assumed to be set (always appends). */ +static int +glob_in_dir (const char *pattern, const char *directory, int flags, + int (*errfunc) (const char *, int), + glob_t *pglob) +{ + size_t dirlen = strlen (directory); + void *stream = NULL; + struct globnames + { + struct globnames *next; + size_t count; + char *name[64]; + }; +#define INITIAL_COUNT sizeof (init_names.name) / sizeof (init_names.name[0]) + struct globnames init_names; + struct globnames *names = &init_names; + struct globnames *names_alloca = &init_names; + size_t nfound = 0; + size_t allocasize = sizeof (init_names); + size_t cur = 0; + int meta; + int save; + + init_names.next = NULL; + init_names.count = INITIAL_COUNT; + + meta = __glob_pattern_type (pattern, !(flags & GLOB_NOESCAPE)); + if (meta == 0 && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC))) + { + /* We need not do any tests. The PATTERN contains no meta + characters and we must not return an error therefore the + result will always contain exactly one name. */ + flags |= GLOB_NOCHECK; + } + else if (meta == 0) + { + /* Since we use the normal file functions we can also use stat() + to verify the file is there. */ + struct stat st; + struct_stat64 st64; + size_t patlen = strlen (pattern); + char *fullname = (char *) __alloca (dirlen + 1 + patlen + 1); + + mempcpy (mempcpy (mempcpy (fullname, directory, dirlen), + "/", 1), + pattern, patlen + 1); + if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? (*pglob->gl_stat) (fullname, &st) + : __stat64 (fullname, &st64)) == 0) + /* We found this file to be existing. Now tell the rest + of the function to copy this name into the result. */ + flags |= GLOB_NOCHECK; + } + else + { + stream = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? (*pglob->gl_opendir) (directory) + : opendir (directory)); + if (stream == NULL) + { + if (errno != ENOTDIR + && ((errfunc != NULL && (*errfunc) (directory, errno)) + || (flags & GLOB_ERR))) + return GLOB_ABORTED; + } + else + { +#ifdef _LIBC + int dfd = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? -1 : dirfd ((DIR *) stream)); +#endif + int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) + | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) +#if defined _AMIGA || defined VMS + | FNM_CASEFOLD +#endif + ); + flags |= GLOB_MAGCHAR; + + while (1) + { + const char *name; + size_t len; +#if defined _LIBC && !defined COMPILE_GLOB64 + struct dirent64 *d; + union + { + struct dirent64 d64; + char room [offsetof (struct dirent64, d_name[0]) + + NAME_MAX + 1]; + } + d64buf; + + if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) + { + struct dirent *d32 = (*pglob->gl_readdir) (stream); + if (d32 != NULL) + { + CONVERT_DIRENT_DIRENT64 (&d64buf.d64, d32); + d = &d64buf.d64; + } + else + d = NULL; + } + else + d = __readdir64 (stream); +#else + struct dirent *d = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? ((struct dirent *) + (*pglob->gl_readdir) (stream)) + : __readdir (stream)); +#endif + if (d == NULL) + break; + if (! REAL_DIR_ENTRY (d)) + continue; + + /* If we shall match only directories use the information + provided by the dirent call if possible. */ + if ((flags & GLOB_ONLYDIR) && !DIRENT_MIGHT_BE_DIR (d)) + continue; + + name = d->d_name; + + if (fnmatch (pattern, name, fnm_flags) == 0) + { + /* If the file we found is a symlink we have to + make sure the target file exists. */ + if (!DIRENT_MIGHT_BE_SYMLINK (d) + || link_exists_p (dfd, directory, dirlen, name, pglob, + flags)) + { + if (cur == names->count) + { + struct globnames *newnames; + size_t count = names->count * 2; + size_t size = (sizeof (struct globnames) + + ((count - INITIAL_COUNT) + * sizeof (char *))); + allocasize += size; + if (__libc_use_alloca (allocasize)) + newnames = names_alloca = __alloca (size); + else if ((newnames = malloc (size)) + == NULL) + goto memory_error; + newnames->count = count; + newnames->next = names; + names = newnames; + cur = 0; + } + len = NAMLEN (d); + names->name[cur] = (char *) malloc (len + 1); + if (names->name[cur] == NULL) + goto memory_error; + *((char *) mempcpy (names->name[cur++], name, len)) + = '\0'; + ++nfound; + } + } + } + } + } + + if (nfound == 0 && (flags & GLOB_NOCHECK)) + { + size_t len = strlen (pattern); + nfound = 1; + names->name[cur] = (char *) malloc (len + 1); + if (names->name[cur] == NULL) + goto memory_error; + *((char *) mempcpy (names->name[cur++], pattern, len)) = '\0'; + } + + int result = GLOB_NOMATCH; + if (nfound != 0) + { + result = 0; + + char **new_gl_pathv; + new_gl_pathv + = (char **) realloc (pglob->gl_pathv, + (pglob->gl_pathc + pglob->gl_offs + nfound + 1) + * sizeof (char *)); + if (new_gl_pathv == NULL) + { + memory_error: + while (1) + { + struct globnames *old = names; +#if 0 + for (size_t i = 0; i < cur; ++i) +#else + size_t i; + for (i = 0; i < cur; ++i) +#endif + free (names->name[i]); + names = names->next; + /* NB: we will not leak memory here if we exit without + freeing the current block assigned to OLD. At least + the very first block is always allocated on the stack + and this is the block assigned to OLD here. */ + if (names == NULL) + { + assert (old == &init_names); + break; + } + cur = names->count; + if (old == names_alloca) + names_alloca = names; + else + free (old); + } + result = GLOB_NOSPACE; + } + else + { + while (1) + { + struct globnames *old = names; +#if 0 + for (size_t i = 0; i < cur; ++i) +#else + size_t i; + for (i = 0; i < cur; ++i) +#endif + new_gl_pathv[pglob->gl_offs + pglob->gl_pathc++] + = names->name[i]; + names = names->next; + /* NB: we will not leak memory here if we exit without + freeing the current block assigned to OLD. At least + the very first block is always allocated on the stack + and this is the block assigned to OLD here. */ + if (names == NULL) + { + assert (old == &init_names); + break; + } + cur = names->count; + if (old == names_alloca) + names_alloca = names; + else + free (old); + } + + pglob->gl_pathv = new_gl_pathv; + + pglob->gl_pathv[pglob->gl_offs + pglob->gl_pathc] = NULL; + + pglob->gl_flags = flags; + } + } + + if (stream != NULL) + { + save = errno; + if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) + (*pglob->gl_closedir) (stream); + else + closedir (stream); + __set_errno (save); + } + + return result; +} diff --git a/preload/glob64.c b/preload/glob64.c new file mode 100644 index 0000000..7c71312 --- /dev/null +++ b/preload/glob64.c @@ -0,0 +1,29 @@ +/* Copied from glibc-2.7 to Scratchbox 2, + * from glibc's sysdep/gnu/glob64.c. +*/ + +#include <dirent.h> +#include <glob.h> +#include <sys/stat.h> + +#define dirent dirent64 +#define __readdir(dirp) __readdir64 (dirp) + +#define glob_t glob64_t +#if 0 +#define glob(pattern, flags, errfunc, pglob) \ + glob64 (pattern, flags, errfunc, pglob) +#endif +#define globfree(pglob) globfree64 (pglob) + +#undef stat +#define stat stat64 +#undef __stat +#define __stat(file, buf) __xstat64 (_STAT_VER, file, buf) + +#define NO_GLOB_PATTERN_P 1 + +#define COMPILE_GLOB64 1 + +#include "glob.c" + diff --git a/preload/interface.master b/preload/interface.master index 8fa98f5..99d6422 100644 --- a/preload/interface.master +++ b/preload/interface.master @@ -63,12 +63,10 @@ GATE: FTS * fts_open (char * const *path_argv, int options, \ int (*compar)(const FTSENT **, const FTSENT **)) #endif GATE: int glob (const char *pattern, int flags, \ - int (*errfunc) (const char *, int), glob_t *pglob) : \ - map(pattern) + int (*errfunc) (const char *, int), glob_t *pglob) : #ifdef HAVE_GLOB64 GATE: int glob64 (const char *pattern, int flags, \ - int (*errfunc) (const char *, int), glob64_t *pglob) : \ - map(pattern) + int (*errfunc) (const char *, int), glob64_t *pglob) : #endif -- These gates just log what happened: @@ -119,9 +117,12 @@ WRAP: int openat64(int dirfd, const char *pathname, int flags, ...) : \ -- --------------- -- -WRAP: int __lxstat(int ver, const char *filename, struct stat *buf) : map(filename) +WRAP: int __lxstat(int ver, const char *filename, struct stat *buf) : \ + dont_resolve_final_symlink map(filename) + #ifdef HAVE___LXSTAT64 -WRAP: int __lxstat64(int ver, const char *filename, struct stat64 *buf) : map(filename) +WRAP: int __lxstat64(int ver, const char *filename, struct stat64 *buf) : \ + dont_resolve_final_symlink map(filename) #endif -- N.B. 2nd parameter of '__opendir2' is bufsize, at least on @@ -129,7 +130,7 @@ WRAP: int __lxstat64(int ver, const char *filename, struct stat64 *buf) : map(fi WRAP: DIR *__opendir2(const char *name, int flags) : map(name) WRAP: int __xmknod(int ver, const char *path, mode_t mode, dev_t *dev) : \ - map(path) fail_if_readonly(path,-1,EROFS) + dont_resolve_final_symlink map(path) fail_if_readonly(path,-1,EROFS) WRAP: int __xstat(int ver, const char *filename, struct stat *buf) : map(filename) #ifdef HAVE___XSTAT64 @@ -224,14 +225,20 @@ WRAP: int getattrlist(const char* path, struct attrlist * attrList, \ map(path) WRAP: ssize_t getxattr(const char *path, const char *name, void *value, size_t size, u_int32_t position, int options) : map(path) #endif -WRAP: int glob_pattern_p(const char *pattern, int quote) : map(pattern) + +EXPORT: int glob_pattern_p(const char *pattern, int quote) + WRAP: int lchmod(const char *path, mode_t mode) : \ - map(path) fail_if_readonly(path,-1,EROFS) + dont_resolve_final_symlink map(path) fail_if_readonly(path,-1,EROFS) + WRAP: int lchown(const char *path, uid_t owner, gid_t group) : \ - map(path) fail_if_readonly(path,-1,EROFS) + dont_resolve_final_symlink map(path) fail_if_readonly(path,-1,EROFS) + #ifdef HAVE_LGETXATTR -WRAP: ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size) : map(path) +WRAP: ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size) : \ + dont_resolve_final_symlink map(path) #endif + WRAP: int link(const char *oldpath, const char *newpath) : \ map(oldpath) map(newpath) \ fail_if_readonly(oldpath,-1,EROFS) \ @@ -250,23 +257,33 @@ WRAP: ssize_t listxattr(const char *path, char *list, size_t size) : map(path) WRAP: ssize_t listxattr(const char *path, char *list, size_t size, int options) : map(path) #endif #endif + #ifdef HAVE_LLISTXATTR -WRAP: ssize_t llistxattr(const char *path, char *list, size_t size) : map(path) +WRAP: ssize_t llistxattr(const char *path, char *list, size_t size) : \ + dont_resolve_final_symlink map(path) #endif + #ifdef HAVE_LREMOVEXATTR WRAP: int lremovexattr(const char *path, const char *name) : \ - map(path) fail_if_readonly(path,-1,EROFS) + dont_resolve_final_symlink map(path) fail_if_readonly(path,-1,EROFS) #endif #ifdef HAVE_LSETXATTR WRAP: int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) : \ - map(path) fail_if_readonly(path,-1,EROFS) + dont_resolve_final_symlink map(path) fail_if_readonly(path,-1,EROFS) #endif -WRAP: int lstat(const char *file_name, struct stat *buf) : map(file_name) + +WRAP: int lstat(const char *file_name, struct stat *buf) : \ + dont_resolve_final_symlink map(file_name) + #ifdef HAVE_LSTAT64 -WRAP: int lstat64(const char *file_name, struct stat64 *buf) : map(file_name) +WRAP: int lstat64(const char *file_name, struct stat64 *buf) : \ + dont_resolve_final_symlink map(file_name) #endif + WRAP: int lutimes(const char *filename, const struct timeval tv[2]) : \ - map(filename) fail_if_readonly(filename,-1,EROFS) + dont_resolve_final_symlink map(filename) \ + fail_if_readonly(filename,-1,EROFS) + WRAP: int mkdir(const char *pathname, mode_t mode) : \ map(pathname) fail_if_readonly(pathname,-1,EROFS) WRAP: int mkdirat(int dirfd, const char *pathname, mode_t mode) : \ @@ -285,8 +302,13 @@ WRAP: int nftw64(const char *dir, int (*fn)(const char *file, const struct stat6 #endif WRAP: DIR *opendir(const char *name) : map(name) WRAP: long pathconf(const char *path, int name) : map(path) -WRAP: READLINK_TYPE readlink(const char *path, char *buf, size_t bufsize) : map(path) -WRAP: READLINK_TYPE readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsize) : map_at(dirfd,pathname) + +WRAP: READLINK_TYPE readlink(const char *path, char *buf, size_t bufsize) : \ + dont_resolve_final_symlink map(path) + +WRAP: READLINK_TYPE readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsize) : \ + dont_resolve_final_symlink map_at(dirfd,pathname) + WRAP: char *realpath(const char *name, char *resolved) : map(name) returns_string WRAP: int remove(const char *pathname) : \ map(pathname) fail_if_readonly(pathname,-1,EROFS) @@ -301,12 +323,14 @@ WRAP: int removexattr(const char *path, const char *name, int options) : \ #endif #endif WRAP: int rename(const char *oldpath, const char *newpath) : \ - map(oldpath) map(newpath) \ + dont_resolve_final_symlink map(oldpath) \ + dont_resolve_final_symlink map(newpath) \ fail_if_readonly(oldpath,-1,EROFS) \ fail_if_readonly(newpath,-1,EROFS) WRAP: int renameat(int olddirfd, const char *oldpath, int newdirfd, \ const char *newpath) : \ - map_at(olddirfd,oldpath) map_at(newdirfd,newpath) \ + dont_resolve_final_symlink map_at(olddirfd,oldpath) \ + dont_resolve_final_symlink map_at(newdirfd,newpath) \ fail_if_readonly(oldpath,-1,EROFS) \ fail_if_readonly(newpath,-1,EROFS) @@ -351,12 +375,15 @@ WRAP: int stat(const char *file_name, struct stat *buf) : map(file_name) WRAP: int stat64(const char *file_name, struct stat64 *buf) : map(file_name) #endif +-- symlink and symlinkat: +-- * "oldpath" is the string that will be contents of the symlink, and +-- it must not mapped now when SB2 resolves symlinks +-- * "newpath" is location where the symlink will be created. WRAP: int symlink(const char *oldpath, const char *newpath) : \ - map(oldpath) map(newpath) \ + dont_resolve_final_symlink map(newpath) \ fail_if_readonly(newpath,-1,EROFS) - WRAP: int symlinkat(const char *oldpath, int newdirfd, const char *newpath) : \ - map(oldpath) map_at(newdirfd,newpath) \ + dont_resolve_final_symlink map_at(newdirfd,newpath) \ fail_if_readonly(newpath,-1,EROFS) WRAP: char *tempnam(const char *dir, const char *pfx) : map(dir) returns_string @@ -368,10 +395,10 @@ WRAP: int truncate64(const char *path, off64_t length) : \ #endif WRAP: int unlink(const char *pathname) : \ - map(pathname) \ + dont_resolve_final_symlink map(pathname) \ fail_if_readonly(pathname,-1,EROFS) WRAP: int unlinkat(int dirfd, const char *pathname, int flags) : \ - map_at(dirfd,pathname) \ + dont_resolve_final_symlink map_at(dirfd,pathname) \ fail_if_readonly(pathname,-1,EROFS) WRAP: int utime(const char *filename, const struct utimbuf *buf) : \ diff --git a/preload/libsb2.c b/preload/libsb2.c index e60bc88..ddd7c45 100644 --- a/preload/libsb2.c +++ b/preload/libsb2.c @@ -329,7 +329,8 @@ FTS * fts_open_gate(FTS * (*real_fts_open_ptr)(char * const *path_argv, for (n=0, p=path_argv, np=new_path_argv; *p; n++, p++, np++) { path = *p; - sbox_path = scratchbox_path(realfnname, path, NULL/*RO-flag*/); + sbox_path = scratchbox_path(realfnname, path, NULL/*RO-flag*/, + 0/*dont_resolve_final_symlink*/); *np = sbox_path; } @@ -348,7 +349,8 @@ char * get_current_dir_name_gate( return NULL; } if (*cwd != '\0') { - sbox_path = scratchbox_path(realfnname, cwd, NULL/*RO-flag*/); + sbox_path = scratchbox_path(realfnname, cwd, NULL/*RO-flag*/, + 0/*dont_resolve_final_symlink*/); } free(cwd); return sbox_path; @@ -369,7 +371,8 @@ char *getcwd_gate ( return NULL; } if (*cwd != '\0') { - sbox_path = scratchbox_path(realfnname, cwd, NULL/*RO-flag*/); + sbox_path = scratchbox_path(realfnname, cwd, NULL/*RO-flag*/, + 0/*dont_resolve_final_symlink*/); } if (sbox_path) { if(buf) { @@ -397,7 +400,8 @@ char * getwd_gate( return NULL; } if (*cwd != '\0') { - sbox_path = scratchbox_path(realfnname, cwd, NULL/*RO-flag*/); + sbox_path = scratchbox_path(realfnname, cwd, NULL/*RO-flag*/, + 0/*dont_resolve_final_symlink*/); } if (sbox_path) { if(buf) { @@ -412,6 +416,35 @@ char * getwd_gate( return cwd; } +/* "SB2_WRAP_GLOB" needs to be defined on systems where the C library + * is not based on glibc (or not compatible with glob() from glibc 2.7) +*/ +#ifdef SB2_WRAP_GLOB +static char *check_and_prepare_glob_pattern( + const char *realfnname, + const char *pattern) +{ + char *mapped__pattern = NULL; + + /* only map pattern if it contains a '/' + * FIXME: this is probably not completely correct way to process this, + * a bit more intelligence could be useful here... that is why we'll + * log the mapped pattern (NOTICE level) if it was mapped + */ + if (*pattern == '/') { /* if absolute path in pattern.. */ + mapped__pattern = scratchbox_path(realfnname, pattern, + NULL/*RO-flag*/, 0/*dont_resolve_final_symlink*/); + if (!strcmp(mapped__pattern, pattern)) { + /* no change */ + free(mapped__pattern); + return(NULL); + } + SB_LOG(SB_LOGLEVEL_NOTICE, "%s: mapped pattern '%s' => '%s'", + realfnname, pattern, mapped__pattern); + } + return(mapped__pattern); +} +#endif int glob_gate( int (*real_glob_ptr)(const char *pattern, int flags, @@ -423,21 +456,31 @@ int glob_gate( glob_t *pglob) { int rc; - unsigned int i; - char tmp[SBOX_MAXPATH]; - - rc = (*real_glob_ptr)(pattern, flags, errfunc, pglob); - - if (rc < 0) return rc; - - for(i = 0; i < pglob->gl_pathc; i++) { - char *sbox_path = NULL; - - strcpy(tmp,pglob->gl_pathv[i]); - sbox_path = scratchbox_path(realfnname, tmp, NULL/*RO-flag*/); - strcpy(pglob->gl_pathv[i], sbox_path); - if (sbox_path) free(sbox_path); +#ifdef SB2_WRAP_GLOB + char *mapped__pattern; + + mapped__pattern = check_and_prepare_glob_pattern(realfnname, pattern); + rc = (*real_glob_ptr)(mapped__pattern ? mapped__pattern : pattern, + flags, errfunc, pglob); + if (mapped__pattern) free(mapped__pattern); +#else + /* glob() has been replaced by a modified copy (from glibc) */ + unsigned int i; + + (void)real_glob_ptr; /* not used */ + + SB_LOG(SB_LOGLEVEL_DEBUG, "%s: pattern='%s' gl_offs=%d, flags=0x%X", + realfnname, pattern, pglob->gl_offs, flags); + rc = do_glob(pattern, flags, errfunc, pglob); + SB_LOG(SB_LOGLEVEL_DEBUG, "%s: returns %d (gl_pathc=%d)", + realfnname, rc, pglob->gl_pathc); + for (i=0; i < pglob->gl_pathc; i++) { + char *cp = pglob->gl_pathv[i + pglob->gl_offs]; + SB_LOG(SB_LOGLEVEL_DEBUG, "%s: [%d='%s']", + realfnname, i, cp ? cp : "<NULL>"); } +#endif + return rc; } @@ -452,21 +495,31 @@ int glob64_gate( glob64_t *pglob) { int rc; +#ifdef SB2_WRAP_GLOB + char *mapped__pattern; + + mapped__pattern = check_and_prepare_glob_pattern(realfnname, pattern); + rc = (*real_glob64_ptr)(mapped__pattern ? mapped__pattern : pattern, + flags, errfunc, pglob); + if (mapped__pattern) free(mapped__pattern); +#else + /* glob64() has been replaced by a modified copy (from glibc) */ unsigned int i; - char tmp[SBOX_MAXPATH]; - rc = (*real_glob64_ptr)(pattern, flags, errfunc, pglob); - - if (rc < 0) return rc; - - for(i = 0; i < pglob->gl_pathc; i++) { - char *sbox_path = NULL; - - strcpy(tmp,pglob->gl_pathv[i]); - sbox_path = scratchbox_path(realfnname, tmp, NULL/*RO-flag*/); - strcpy(pglob->gl_pathv[i], sbox_path); - if (sbox_path) free(sbox_path); + (void)real_glob64_ptr; /* not used */ + + SB_LOG(SB_LOGLEVEL_DEBUG, "%s: pattern='%s' gl_offs=%d, flags=0x%X", + realfnname, pattern, pglob->gl_offs, flags); + rc = do_glob64(pattern, flags, errfunc, pglob); + SB_LOG(SB_LOGLEVEL_DEBUG, "%s: returns %d (gl_pathc=%d)", + realfnname, rc, pglob->gl_pathc); + for (i=0; i < pglob->gl_pathc; i++) { + char *cp = pglob->gl_pathv[i + pglob->gl_offs]; + SB_LOG(SB_LOGLEVEL_DEBUG, "%s: [%d='%s']", + realfnname, i, cp ? cp : "<NULL>"); } +#endif + return rc; } #endif @@ -561,7 +614,7 @@ static void map_sockaddr_un( SB_LOG(SB_LOGLEVEL_DEBUG, "%s: checking AF_UNIX addr '%s'", realfnname, orig_serv_addr_un->sun_path); SBOX_MAP_PATH(orig_serv_addr_un->sun_path, mapped_addr, - &pathname_is_readonly); + &pathname_is_readonly, 0/*dont_resolve_final_symlink*/); /* FIXME: implement if(pathname_is_readonly!=0)... */ *mapped_serv_addr_un = *orig_serv_addr_un; @@ -638,6 +691,9 @@ void *sbox_find_next_symbol(int log_enabled, const char *fn_name) PACKAGE_NAME, fn_name, msg); assert(0); } + if (log_enabled) + SB_LOG(SB_LOGLEVEL_NOISE, "%s: %s at 0x%X", __func__, + fn_name, (int)fn_ptr); return(fn_ptr); } @@ -649,7 +705,8 @@ char *sb2show__map_path2__(const char *binary_name, const char *mapping_mode, if (pathname != NULL) { mapped__pathname = scratchbox_path3(binary_name, fn_name, - pathname, mapping_mode, readonly); + pathname, mapping_mode, readonly, + 0/*dont_resolve_final_symlink*/); } SB_LOG(SB_LOGLEVEL_DEBUG, "%s '%s'", __func__, pathname); return(mapped__pathname); diff --git a/preload/libsb2.h b/preload/libsb2.h index 5d294af..2e49a7b 100644 --- a/preload/libsb2.h +++ b/preload/libsb2.h @@ -72,6 +72,8 @@ #include <sys/socket.h> #include <sys/un.h> +#include <glob.h> + #include <mapping.h> #include <sb2.h> @@ -103,19 +105,19 @@ } \ } -#define SBOX_MAP_PATH(path, sbox_path, readonly_flag_addr) \ +#define SBOX_MAP_PATH(path, sbox_path, readonly_flag_addr, no_symlink_resolve) \ { \ if ((path) != NULL) { \ - sbox_path = scratchbox_path(__FUNCTION__, path, readonly_flag_addr); \ + sbox_path = scratchbox_path(__FUNCTION__, path, readonly_flag_addr, no_symlink_resolve); \ } \ } -#define SBOX_MAP_PATH_AT(dirfd, path, sbox_path, readonly_flag_addr) \ +#define SBOX_MAP_PATH_AT(dirfd, path, sbox_path, readonly_flag_addr, no_symlink_resolve) \ { \ if ((path) != NULL) { \ if (path[0] == '/') { \ /* absolute path */ \ - sbox_path = scratchbox_path(__FUNCTION__, path, readonly_flag_addr); \ + sbox_path = scratchbox_path(__FUNCTION__, path, readonly_flag_addr, no_symlink_resolve); \ } else { \ sbox_path = strdup(path); \ }\ @@ -131,5 +133,12 @@ extern void *sbox_find_next_symbol(int log_enabled, const char *functname); extern int fopen_mode_w_perm(const char *mode); extern int freopen_errno(FILE *stream); +extern int do_glob (const char *pattern, int flags, + int (*errfunc) (const char *, int), glob_t *pglob); +#ifdef HAVE_GLOB64 +extern int do_glob64 (const char *pattern, int flags, + int (*errfunc) (const char *, int), glob64_t *pglob); +#endif + #endif /* ifndef LIBSB2_H_INCLUDED_ */ diff --git a/preload/sb_exec.c b/preload/sb_exec.c index 43395f4..785f202 100644 --- a/preload/sb_exec.c +++ b/preload/sb_exec.c @@ -677,7 +677,7 @@ static int run_hashbang( } mapped_interpreter = scratchbox_path("execve", interpreter, - NULL/*RO-flag addr.*/); + NULL/*RO-flag addr.*/, 0/*dont_resolve_final_symlink*/); SB_LOG(SB_LOGLEVEL_DEBUG, "run_hashbang(): interpreter=%s," "mapped_interpreter=%s", interpreter, mapped_interpreter); @@ -823,6 +823,19 @@ static void compare_and_log_strvec_changes(const char *vecname, } } +static int strvec_contains(char *const *strvec, const char *id) +{ + char *const *ptr2vec = strvec; + + while (*ptr2vec) { + if (!strcmp(*ptr2vec, id)) { + return(1); + } + ptr2vec++; + } + return(0); +} + int do_exec(const char *exec_fn_name, const char *file, char *const *argv, char *const *envp) { @@ -865,18 +878,28 @@ int do_exec(const char *exec_fn_name, const char *file, compare_and_log_strvec_changes("envp", *my_envp_copy, *my_envp); } - /* now we have to do path mapping for *my_file to find exactly - * what is the path we're supposed to deal with - */ + /* test if mapping is enabled during the exec().. + * (host-* tools disable it) + */ + if (strvec_contains(*my_envp, "SBOX_DISABLE_MAPPING=1")) { + SB_LOG(SB_LOGLEVEL_DEBUG, + "do_exec(): mapping disabled, *my_file = %s", + *my_file); + mapped_file = strdup(*my_file); + } else { + /* now we have to do path mapping for *my_file to find exactly + * what is the path we're supposed to deal with + */ - mapped_file = scratchbox_path("do_exec", *my_file, - NULL/*RO-flag addr.*/); - SB_LOG(SB_LOGLEVEL_DEBUG, - "do_exec(): *my_file = %s, mapped_file = %s", - *my_file, mapped_file); + mapped_file = scratchbox_path("do_exec", *my_file, + NULL/*RO-flag addr.*/, 0/*dont_resolve_final_symlink*/); + SB_LOG(SB_LOGLEVEL_DEBUG, + "do_exec(): *my_file = %s, mapped_file = %s", + *my_file, mapped_file); + } - type = inspect_binary(mapped_file); /* inspect the completely mangled - * filename */ + /* inspect the completely mangled filename */ + type = inspect_binary(mapped_file); switch (type) { case BIN_HASHBANG: diff --git a/utils/debconf2po-update b/utils/debconf2po-update new file mode 100644 index 0000000..d897f0a --- /dev/null +++ b/utils/debconf2po-update @@ -0,0 +1,9 @@ +#!/bin/bash + +echo "Warning: debconf2po-update has been replaced by debconf-updatepo a long" +echo " time ago, and transition phase is over. You should update your" +echo " config to use the new name... SB2 is going to execute" +echo " 'debconf-updatepo' now." + +exec debconf-updatepo $* + diff --git a/utils/dpkg-checkbuilddeps b/utils/dpkg-checkbuilddeps index 8840b2e..eae2974 100755 --- a/utils/dpkg-checkbuilddeps +++ b/utils/dpkg-checkbuilddeps @@ -105,6 +105,7 @@ function check_host_pkg() if [ -n "$g" ] then check_result_msg="'$pkgname' is not available (ignored by configuration)" + installed_to_host="yes" return fi fi @@ -19,7 +19,7 @@ If no COMMAND is given, a bash shell in scratchbox2 environment is started. Options: -v display version - -L level enable logging (levels=one of error,warning,notice,info,debug,noise) + -L level enable logging (levels=one of error,warning,notice,info,debug,noise,noise2) -d debug mode: log all redirections (logging level=debug) -h print this help -t TARGET target to use, use sb2-config -d TARGET to set a default @@ -78,7 +78,7 @@ function sboxify_environment() . ~/.scratchbox2/$SBOX_TARGET/sb2.config - if [ "$SBOX_CONFIG_VERSION" != "5" ]; then + if [ "$SBOX_CONFIG_VERSION" != "7" ]; then echo "Please run sb2-init for your target:" echo "name: $SBOX_TARGET" echo "dir: $SBOX_TARGET_ROOT" @@ -166,7 +166,7 @@ shift $(($OPTIND - 1)) if [ "$SBOX_MAPPING_DEBUG" == "1" ]; then # check that loglevel is valid case $SBOX_MAPPING_LOGLEVEL in - (error|warning|notice|info|debug|noise) ;; # OK + (error|warning|notice|info|debug|noise|noise2) ;; # OK (*) usage ;; esac else diff --git a/utils/sb2-init b/utils/sb2-init index 4f6f7cf..0b5dfde 100755 --- a/utils/sb2-init +++ b/utils/sb2-init @@ -92,7 +92,7 @@ SBOX_INIT_ORIG_ARGS="$SBOX_INIT_ORIG_ARGS" SBOX_INIT_TIME=$SBOX_INIT_TIME SBOX_INIT_ID="$SBOX_INIT_ID" -export SBOX_CONFIG_VERSION=5 +export SBOX_CONFIG_VERSION=7 export SBOX_TARGET_ROOT=$SBOX_TARGET_ROOT export SBOX_CPU=$ARCH export SBOX_GCC_TARGET=$GCC_TARGET @@ -102,24 +102,29 @@ export SBOX_UNAME_MACHINE=$ARCH export SBOX_DEFAULT_GCC_PREFIX=$GCC_PREFIX export SBOX_CROSS_GCC_NAME=cross-gcc -export SBOX_CROSS_GCC_PREFIX_LIST=$GCC_TARGET-:$ARCH-linux-:$GCC_PREFIX +export SBOX_CROSS_GCC_PREFIX_LIST=$GCC_TARGET-:$SB2INIT_CROSS_GCC_PREFIX_LIST:$GCC_PREFIX export SBOX_CROSS_GCC_SUBST_PREFIX=$GCC_PREFIX -export SBOX_CROSS_GCC_SPECS_FILE= +export SBOX_CROSS_GCC_SPECS_FILE="$SBOX_CROSS_GCC_SPECS_FILE" export SBOX_CROSS_GCC_DIR=$GCC_PATH export SBOX_CROSS_GCC_LD_ARGS= export SBOX_EXTRA_CROSS_COMPILER_ARGS="$SBOX_EXTRA_CROSS_COMPILER_ARGS" export SBOX_EXTRA_CROSS_COMPILER_STDINC="$SBOX_EXTRA_CROSS_COMPILER_STDINC" export SBOX_EXTRA_CROSS_LD_ARGS="-rpath-link $SBOX_TARGET_ROOT/usr/lib:$SBOX_TARGET_ROOT/lib" -export DEB_BUILD_ARCH_CPU=$DEBIAN_CPU -export DEB_BUILD_ARCH=$DEBIAN_CPU +export DEB_BUILD_ARCH=$SB2INIT_DEB_BUILD_ARCH +export DEB_BUILD_ARCH_CPU=$SB2INIT_DEB_BUILD_ARCH_CPU +export DEB_BUILD_ARCH_ABI=$SB2INIT_DEB_BUILD_ARCH_ABI export DEB_BUILD_GNU_CPU=$ARCH -export DEB_BUILD_GNU_TYPE=$ARCH-linux-gnu +export DEB_BUILD_GNU_TYPE=$SB2INIT_DEB_BUILD_GNU_TYPE +export DEB_BUILD_GNU_SYSTEM=$SB2INIT_DEB_BUILD_GNU_SYSTEM -export DEB_HOST_ARCH_CPU=$DEBIAN_CPU export DEB_HOST_ARCH=$DEBIAN_CPU +export DEB_HOST_ARCH_OS=$SB2INIT_DEB_HOST_ARCH_OS +export DEB_HOST_ARCH_CPU=$SB2INIT_DEB_HOST_ARCH_CPU + export DEB_HOST_GNU_CPU=$ARCH -export DEB_HOST_GNU_TYPE=$ARCH-linux-gnu +export DEB_HOST_GNU_TYPE=$SB2INIT_DEB_HOST_GNU_TYPE +export DEB_HOST_GNU_SYSTEM=$SB2INIT_DEB_HOST_GNU_SYSTEM export SBOX_HOST_GCC_NAME=host-gcc export SBOX_HOST_GCC_PREFIX_LIST=host- @@ -284,6 +289,7 @@ else fi fi + DEBIAN_CPU=$ARCH if [ -z "$CPUTRANSP" ]; then CPUTRANSP="$(which qemu-$ARCH)" @@ -304,6 +310,10 @@ case "$ARCH" in ppc*) ;; mips*) ;; sh*) ;; + i386*) ;; + i486*) ;; + i586*) ;; + i686*) ;; *) echo "Unsupported target architecture: $ARCH" echo "You must add support for it into preload/sb_exec.c" @@ -313,6 +323,29 @@ case "$ARCH" in esac +# defaults for SB2_INIT_DEB_BUILD_GNU_TYPE, SB2_INIT_DEB_HOST_GNU_TYPE, etc, +# and the cross-gcc prefix list: +# these may be changed by sb2rc.$MAPPING_MODE +SB2INIT_DEB_BUILD_ARCH=$DEBIAN_CPU +SB2INIT_DEB_BUILD_ARCH_ABI="gnu" +SB2INIT_DEB_BUILD_GNU_TYPE=$ARCH-linux-gnu +SB2INIT_DEB_BUILD_GNU_SYSTEM="" +SB2INIT_DEB_BUILD_ARCH_CPU=$DEBIAN_CPU + +SB2INIT_DEB_HOST_GNU_TYPE=$ARCH-linux-gnu +SB2INIT_DEB_HOST_GNU_SYSTEM="" +SB2INIT_DEB_HOST_ARCH_OS="linux" +SB2INIT_DEB_HOST_ARCH_CPU=$DEBIAN_CPU + +SB2INIT_CROSS_GCC_PREFIX_LIST=$ARCH-linux- + +# $ARCH has been set, get mode-specific settings.. +if [ -f $SBOX_DIR/share/scratchbox2/modeconf/sb2rc.$MAPPING_MODE ] +then + echo "Reading mode-specific settings.." + . $SBOX_DIR/share/scratchbox2/modeconf/sb2rc.$MAPPING_MODE +fi + if [ -z "$MAPPING_MODE" ]; then MAPPING_MODE="simple" fi @@ -350,7 +383,16 @@ if [ -n "$TOOLS_ROOT" ]; then fi fi -SBOX_EXTRA_CROSS_COMPILER_STDINC="-I/usr/include" +# Use "specs" file for gcc if it exists, otherwise add -I/usr/include to params +SBOX_CROSS_GCC_SPECS_FILE="" +SBOX_EXTRA_CROSS_COMPILER_STDINC="" +if [ -f $SBOX_DIR/share/scratchbox2/modeconf/gcc-specs.$MAPPING_MODE ] +then + SBOX_CROSS_GCC_SPECS_FILE="$SBOX_DIR/share/scratchbox2/modeconf/gcc-specs.$MAPPING_MODE" +else + SBOX_EXTRA_CROSS_COMPILER_STDINC="-I/usr/include" +fi + SBOX_EXTRA_CROSS_COMPILER_ARGS="$SBOX_EXTRA_CROSS_COMPILER_ARGS -L$SBOX_TARGET_ROOT/usr/lib -L$SBOX_TARGET_ROOT/lib" # test if the cross compiler needs to be silenced about /usr/include |