summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLauri Leukkunen <lle@rahina.org>2008-06-17 10:09:23 +0300
committerLauri Leukkunen <lle@rahina.org>2008-06-17 10:09:23 +0300
commitb26b8030110fd2f225412c26bed2f44849563ed0 (patch)
tree99caab52e9761c9ee5afef4566bdf3497ebabf7a
parent445f646df2614e0fa2aa6ba22da56c94e4d77b10 (diff)
parent312541a4e77f89def79c211d92010e533f3f5d30 (diff)
Merge branch 'lle'1.99.0.25
-rw-r--r--Makefile3
-rw-r--r--debian/changelog4
-rw-r--r--include/mapping.h9
-rw-r--r--include/sb2.h1
-rw-r--r--lua_scripts/argvenvp_gcc.lua4
-rw-r--r--lua_scripts/mapping.lua207
-rw-r--r--lua_scripts/pathmaps/maemo/00_default.lua38
-rw-r--r--luaif/luaif.c19
-rw-r--r--luaif/paths.c789
-rw-r--r--luaif/sb_log.c3
-rw-r--r--modeconf/gcc-specs.maemo9
-rw-r--r--modeconf/sb2rc.maemo19
-rw-r--r--preload/Makefile2
-rwxr-xr-xpreload/gen-interface.pl17
-rw-r--r--preload/glob.c1557
-rw-r--r--preload/glob64.c29
-rw-r--r--preload/interface.master79
-rw-r--r--preload/libsb2.c121
-rw-r--r--preload/libsb2.h17
-rw-r--r--preload/sb_exec.c45
-rw-r--r--utils/debconf2po-update9
-rwxr-xr-xutils/dpkg-checkbuilddeps1
-rwxr-xr-xutils/sb26
-rwxr-xr-xutils/sb2-init60
24 files changed, 2720 insertions, 328 deletions
diff --git a/Makefile b/Makefile
index f101629..0910fef 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/utils/sb2 b/utils/sb2
index d1feda7..5f3f48e 100755
--- a/utils/sb2
+++ b/utils/sb2
@@ -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