summaryrefslogtreecommitdiff
path: root/redir_scripts/main.lua
blob: 6eaf66ec0940176ac2a54697a4ad864b7077315e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
-- Scratchbox2 universal redirector dynamic path translation scripts
-- Copyright (C) 2006, 2007 Lauri Leukkunen
-- Licensed under MIT license.

tools_root = os.getenv("SBOX_TOOLS_ROOT")
if (not tools_root) then
	tools_root = "/scratchbox/sarge"
end

target_root = os.getenv("SBOX_TARGET_ROOT")
if (not target_root) then
	target_root = "/"
end

compiler_root = os.getenv("SBOX_COMPILER_ROOT")
if (not compiler_root) then
	compiler_root = "/usr"
end

verbose = os.getenv("SBOX_MAPPING_VERBOSE")


-- SBOX_REDIR_SCRIPTS environment variable controls where
-- we look for the scriptlets defining the path mappings

rsdir = os.getenv("SBOX_REDIR_SCRIPTS")
if (rsdir == nil) then
	rsdir = "/scratchbox/redir_scripts"
end


function read_mode_part(mode, part)
	filename = rsdir .. "/preload/" .. mode .. "/" .. part
	f, err = loadfile(filename)
	if (f == nil) then
		error("\nError while loading " .. filename .. ": \n" .. err .. "\n")
	else
		f() -- execute the loaded chunk
		-- export_chains variable contains now the chains
		-- from the chunk
		for i = 1,table.maxn(export_chains) do
			-- fill in the default values
			if (not export_chains[i].binary) then
				export_chains[i].binary = ".*"
			end
			if (not export_chains[i].rules) then
				export_chains[i].rules = {}
			end
			-- loop through the rules
			for r = 1, table.maxn(export_chains[i].rules) do
				if (not export_chains[i].rules[r].func_name) then
					export_chains[i].rules[r].func_name = ".*"
				end
				if (not export_chains[i].rules[r].path) then
					-- this is an error, report and exit
					os.exit(1)
				end
				export_chains[i].rules[r].lua_script = filename
				if (export_chains[i].binary) then
					export_chains[i].rules[r].binary_name = export_chains[i].binary
				else
					export_chains[i].rules[r].binary_name = "nil"
				end
			end
			export_chains[i].lua_script = filename
			table.insert(modes[mode].chains, export_chains[i])
		end
	end
end

-- modes represent the different mapping modes supported.
-- Each mode contains its own set of chains.
-- The mode is passed from the libsb2.so to here in the first
-- argument to sbox_translate_path()
modes = {}

-- sb.sb_getdirlisting is provided by lua_bindings.c
-- it returns a table listing all files in a directory
mm = sb.sb_getdirlisting(rsdir .. "/preload")
table.sort(mm);
for m = 1, table.maxn(mm) do
	local t = sb.sb_getdirlisting(rsdir .. "/preload/" .. mm[m])
	local i = 0
	local r = 0
	if (mm[m] ~= "." and mm[m] ~= "..") then
		table.sort(t)
		modes[mm[m]] = {}
		modes[mm[m]].chains = {}
		-- load the individual parts ($SBOX_REDIR_SCRIPTS/preload/[modename]/*.lua)
		for n = 1,table.maxn(t) do
			if (string.match(t[n], "%a*%.lua$")) then
				read_mode_part(mm[m], t[n])
			end
		end
	end
end


function basename(path)
	if (path == "/") then
		return "/"
	else
		return string.match(path, "[^/]*$")
	end
end

function dirname(path)
	if (path == "/") then
		return "/"
	end
	dir = string.match(path, ".*/")
	if (dir == nil) then
		return "."
	end

	if (dir == "/") then return dir end

	-- chop off the trailing /
	if (string.sub(dir, string.len(dir)) == "/") then
		dir = string.sub(dir, 1, string.len(dir) - 1)
	end
	return dir 
end


function sb_debug(msg)
	local logfile = os.getenv("SBOX_MAPPING_LOGFILE")
	local f
	local err
	if (not logfile) then return end
	f, err = io.open(logfile, "a+")
	if (not f) then return end
	f:write(msg .. "\n")
	io.close(f)
end

function adjust_for_mapping_leakage(path)
	if (not path) then 
		return nil
	end
	--print("path: " .. path)
	local tmp = sb.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
	-- decolonize it
	tmp = sb.sb_decolonize_path(tmp)
	--print(string.format("after decolonizing: %s\n", tmp))
	if (not string.match(tmp, "^" .. target_root .. ".*")) then
		-- aha! tried to get out of there, now map it right back in
		return adjust_for_mapping_leakage(target_root .. tmp)
	else
		return adjust_for_mapping_leakage(tmp)
	end
end

function sbox_map_to(binary_name, func_name, work_dir, rp, path, rule)
	local ret = nil
	if (rule.map_to) then
		if (string.sub(rule.map_to, 1, 1) == "=") then
			--print(string.format("rp: %s\ntarget_root: %s", rp, target_root))
			ret = target_root .. string.sub(rule.map_to, 2) .. path
		elseif (string.sub(rule.map_to, 1, 1) == "-") then
			ret = string.match(path, rule.path .. "(.*)")
			ret = string.sub(rule.map_to, 2) .. ret
		else
			ret = rule.map_to .. path
		end
		return adjust_for_mapping_leakage(ret)
	end
	-- if not mapping, check if we're within the 
	-- target_root and adjust for mapping leakage if so
	if (string.match(path, "^" .. target_root .. ".*")) then
		return adjust_for_mapping_leakage(path)
	else
		return path
	end
end


function find_rule(chain, func, path)
	local i = 0
	local wrk = chain
	while (wrk) do
		-- travel the chains
		for i = 1, table.maxn(wrk.rules) do
			-- loop the rules in a chain
			if (string.match(func, wrk.rules[i].func_name) and
				string.match(path, wrk.rules[i].path)) then
				return wrk.rules[i]
			end
		end
		wrk = wrk.next_chain
	end
	return nil
end


function map_using_chain(chain, binary_name, func_name, work_dir, path)
	local ret = path
	local rp = path
	local rule = nil

	-- print(string.format("looping through chains: %s", chains[n].binary))
	rule = find_rule(chain, func_name, rp)
	if (not rule) then
		-- error, not even a default rule found
		sb_debug(string.format("Unable to find a match at all: [%s][%s][%s]", binary_name, func_name, path))
		return path
	end
	if (rule.custom_map_func ~= nil) then
		ret = rule.custom_map_func(binary_name, func_name, work_dir, rp, path, rules[n])
	else
		ret = sbox_map_to(binary_name, func_name, work_dir, rp, path, rule)
		if (verbose) then
			sb_debug(string.format("[%s][%s|%s]:\n  %s(%s) -> (%s)", basename(rule.lua_script), rule.binary_name, binary_name, func_name, path, ret))
		end
	end
	return ret
end

-- sbox_translate_path is the function called from libsb2.so
-- preload library and the FUSE system for each path that needs 
-- translating
function sbox_translate_path(mapping_mode, binary_name, func_name, work_dir, path)
	--sb_debug(string.format("[%s]:", binary_name))
	--sb_debug(string.format("debug: [%s][%s][%s][%s]", binary_name, func_name, work_dir, 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 string.match(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)
		end
	end

	-- we should never ever get here, if we still do, don't do anything
	return path
end