/ tests / lib / lua.c
lua.c
  1  /*
  2   * Copyright (c) 2021 Baptiste Daroussin <bapt@FreeBSD.org>
  3   *
  4   * Redistribution and use in source and binary forms, with or without
  5   * modification, are permitted provided that the following conditions
  6   * are met:
  7   * 1. Redistributions of source code must retain the above copyright
  8   *    notice, this list of conditions and the following disclaimer
  9   *    in this position and unchanged.
 10   * 2. Redistributions in binary form must reproduce the above copyright
 11   *    notice, this list of conditions and the following disclaimer in the
 12   *    documentation and/or other materials provided with the distribution.
 13   *
 14   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
 15   * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 16   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 17   * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
 18   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 19   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 20   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 21   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 22   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 23   * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 24   */
 25  
 26  #include <sys/stat.h>
 27  
 28  #include <atf-c.h>
 29  #include <err.h>
 30  #include <unistd.h>
 31  #include <pkg.h>
 32  #include <private/lua.h>
 33  #include <fcntl.h>
 34  #include <stdlib.h>
 35  #include <errno.h>
 36  
 37  
 38  // Some Lua tests are reported on macOS/aarch64 to poison addresses
 39  // e.g.
 40  // AddressSanitizer: CHECK failed: asan_poisoning.cpp:40 "((AddrIsInMem(addr + size - (1ULL << 3)))) != (0)" (0x0, 0x0) (tid=78712)
 41  // #0 0x000101cf086c in __asan::CheckUnwind()+0x20 (libclang_rt.asan_osx_dynamic.dylib:arm64+0x5c86c)
 42  // #1 0x000101d0b318 in __sanitizer::CheckFailed(char const*, int, char const*, unsigned long long, unsigned long long)+0x90 (libclang_rt.asan_osx_dynamic.dylib:arm64+0x77318)
 43  // #2 0x000101ce7ed4 in __asan::PoisonShadow(unsigned long, unsigned long, unsigned char)+0x28c (libclang_rt.asan_osx_dynamic.dylib:arm64+0x53ed4)
 44  // #3 0x000101ce9e74 in __asan::PlatformUnpoisonStacks()+0x50 (libclang_rt.asan_osx_dynamic.dylib:arm64+0x55e74)
 45  // #4 0x000101cf04c0 in __asan_handle_no_return+0x28 (libclang_rt.asan_osx_dynamic.dylib:arm64+0x5c4c0)
 46  // #5 0x00010046cf98 in atfu_execute_body lua.c:193
 47  // #6 0x0001010a70c4 in atf_tc_run+0x58 (libatf-c.1.dylib:arm64+0x30c4)
 48  // #7 0x0001010ad49c in atf_tp_main+0x8a0 (libatf-c.1.dylib:arm64+0x949c)
 49  // #8 0x00010046af48 in main lua.c:491
 50  // #9 0x000187ee4270 ()
 51  // Use this attribute to silence the report until it is better understood. The code itself seems legit.
 52  #if defined(__clang__) || defined (__GNUC__)
 53  # define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
 54  #else
 55  # define ATTRIBUTE_NO_SANITIZE_ADDRESS
 56  #endif
 57  
 58  ATF_TC_WITHOUT_HEAD(readdir);
 59  ATF_TC_WITHOUT_HEAD(stat);
 60  ATF_TC_WITHOUT_HEAD(print_msg);
 61  ATF_TC_WITHOUT_HEAD(execute);
 62  ATF_TC_WITHOUT_HEAD(override);
 63  ATF_TC_WITHOUT_HEAD(fileops);
 64  ATF_TC_WITHOUT_HEAD(prefix_path);
 65  
 66  ATF_TC_BODY(readdir, tc)
 67  {
 68  	char *cwd = getcwd(NULL, 0);
 69  	int rootfd = open(cwd, O_DIRECTORY);
 70  	free(cwd);
 71  	lua_State *L = luaL_newstate();
 72  	static const luaL_Reg test_lib[] = {
 73  		{ "readdir", lua_readdir },
 74  		{ NULL, NULL },
 75  	};
 76  	luaL_openlibs(L);
 77  	lua_override_ios(L, false);
 78  	luaL_newlib(L, test_lib);
 79  	lua_setglobal(L, "test");
 80  	lua_pushinteger(L, rootfd);
 81  	lua_setglobal(L, "rootfd");
 82  
 83  	ATF_REQUIRE(luaL_dostring(L, "test.readdir(\".\", \"plop\")") != 0);
 84  	ATF_REQUIRE_STREQ(lua_tostring(L, -1), "[string \"test.readdir(\".\", \"plop\")\"]:1: bad argument #2 to 'readdir' (pkg.readdir takes exactly one argument)");
 85  	ATF_REQUIRE_EQ(lua_tonumber(L, -1), 0);
 86  
 87  	ATF_REQUIRE(luaL_dostring(L, "test.readdir()") != 0);
 88  	ATF_REQUIRE_STREQ(lua_tostring(L, -1), "[string \"test.readdir()\"]:1: bad argument #0 to 'readdir' (pkg.readdir takes exactly one argument)");
 89  	ATF_REQUIRE_EQ(lua_tonumber(L, -1), 0);
 90  
 91  	ATF_REQUIRE(luaL_dostring(L, "res = test.readdir(\".\")\n gr = #res\n") == 0);
 92  	lua_getglobal(L, "res");
 93  	ATF_REQUIRE(lua_istable(L, -1));
 94  	ATF_REQUIRE_EQ(luaL_len(L, -1), 0);
 95  
 96  	ATF_REQUIRE(luaL_dostring(L, "res = test.readdir(\"nonexistent\")") == 0);
 97  	lua_getglobal(L, "res");
 98  	ATF_REQUIRE(lua_isnil(L, -1));
 99  
100  	ATF_REQUIRE(luaL_dostring(L, "res = test.readdir(\"/\")") == 0);
101  	lua_getglobal(L, "res");
102  	ATF_REQUIRE(!lua_isnil(L, -1));
103  
104  	close(open("testfile", O_CREAT|O_TRUNC, 0644));
105  	ATF_REQUIRE(luaL_dostring(L, "res = test.readdir(\".\")\n gr = #res\n") == 0);
106  	lua_getglobal(L, "res");
107  	ATF_REQUIRE(lua_istable(L, -1));
108  	ATF_REQUIRE_EQ(luaL_len(L, -1), 1);
109  
110  	ATF_REQUIRE(luaL_dostring(L, "res = test.readdir(\"testfile\")") == 0);
111  	lua_getglobal(L, "res");
112  	ATF_REQUIRE(lua_isnil(L, -1));
113  }
114  
115  ATF_TC_BODY(stat, tc)
116  {
117  	char *cwd = getcwd(NULL, 0);
118  	int rootfd = open(cwd, O_DIRECTORY);
119  	free(cwd);
120  	lua_State *L = luaL_newstate();
121  	static const luaL_Reg test_lib[] = {
122  		{ "stat", lua_stat },
123  		{ NULL, NULL },
124  	};
125  	luaL_openlibs(L);
126  	lua_override_ios(L, false);
127  	luaL_newlib(L, test_lib);
128  	lua_setglobal(L, "test");
129  	lua_pushinteger(L, rootfd);
130  	lua_setglobal(L, "rootfd");
131  
132  	ATF_REQUIRE(luaL_dostring(L, "test.stat(\".\", \"plop\")") != 0);
133  	ATF_REQUIRE_STREQ(lua_tostring(L, -1), "[string \"test.stat(\".\", \"plop\")\"]:1: bad argument #2 to 'stat' (pkg.stat takes exactly one argument)");
134  	ATF_REQUIRE_EQ(lua_tonumber(L, -1), 0);
135  
136  	ATF_REQUIRE(luaL_dostring(L, "test.stat()") != 0);
137  	ATF_REQUIRE_STREQ(lua_tostring(L, -1), "[string \"test.stat()\"]:1: bad argument #0 to 'stat' (pkg.stat takes exactly one argument)");
138  	ATF_REQUIRE_EQ(lua_tonumber(L, -1), 0);
139  
140  	ATF_REQUIRE(luaL_dostring(L, "st = test.stat(\".\")\nres = st.type") == 0);
141  	lua_getglobal(L, "res");
142  	ATF_REQUIRE(lua_isstring(L, -1));
143  	ATF_REQUIRE_STREQ(lua_tostring(L,-1), "dir");
144  
145  	close(open("testfile", O_CREAT|O_TRUNC, 0644));
146  	ATF_REQUIRE(luaL_dostring(L, "st = test.stat(\"testfile\")\nres = st.type") == 0);
147  	lua_getglobal(L, "res");
148  	ATF_REQUIRE(lua_isstring(L, -1));
149  	ATF_REQUIRE_STREQ(lua_tostring(L,-1), "reg");
150  
151  	symlink("testfile", "plop");
152  	ATF_REQUIRE(luaL_dostring(L, "st = test.stat(\"plop\")\nres = st.type") == 0);
153  	lua_getglobal(L, "res");
154  	ATF_REQUIRE(lua_isstring(L, -1));
155  	ATF_REQUIRE_STREQ(lua_tostring(L,-1), "lnk");
156  
157  	lua_pushinteger(L, -1);
158  	lua_setglobal(L, "rootfd");
159  	ATF_REQUIRE(luaL_dostring(L, "res = test.stat(\".\")\n") == 0);
160  	lua_getglobal(L, "res");
161  	ATF_REQUIRE(lua_isnil(L, -1));
162  }
163  
164  ATF_TC_BODY(print_msg, tc)
165  {
166  	lua_State *L = luaL_newstate();
167  	static const luaL_Reg test_lib[] = {
168  		{ "print_msg", lua_print_msg },
169  		{ NULL, NULL },
170  	};
171  	int fd = open("testfile", O_CREAT|O_TRUNC|O_WRONLY, 0644);
172  
173  	ATF_REQUIRE_MSG(-1 != fd, "open failed (%d,%s)", errno, strerror(errno));
174  
175  	luaL_openlibs(L);
176  	lua_override_ios(L, false);
177  	luaL_newlib(L, test_lib);
178  	lua_setglobal(L, "test");
179  	lua_pushinteger(L, fd);
180  	lua_setglobal(L, "msgfd");
181  
182  	ATF_REQUIRE(luaL_dostring(L, "test.print_msg()") != 0);
183  	ATF_REQUIRE_STREQ(lua_tostring(L, -1), "[string \"test.print_msg()\"]:1: bad argument #0 to 'print_msg' (pkg.print_msg takes exactly one argument)");
184  	ATF_REQUIRE_EQ(lua_tonumber(L, -1), 0);
185  
186  	ATF_REQUIRE(luaL_dostring(L, "test.print_msg(1, 2)") != 0);
187  	ATF_REQUIRE_STREQ(lua_tostring(L, -1), "[string \"test.print_msg(1, 2)\"]:1: bad argument #2 to 'print_msg' (pkg.print_msg takes exactly one argument)");
188  	ATF_REQUIRE_EQ(lua_tonumber(L, -1), 0);
189  
190  	ATF_REQUIRE(luaL_dostring(L, "test.print_msg(\"bla\")") == 0);
191  	int err = close(fd);
192  	ATF_REQUIRE_MSG(0 == err, "close failed (%d,%s)", errno, strerror(errno));
193  
194  	atf_utils_compare_file("testfile", "bla\n");
195  }
196  
197  ATTRIBUTE_NO_SANITIZE_ADDRESS ATF_TC_BODY(execute, tc)
198  {
199  	lua_State *L = luaL_newstate();
200  	static const luaL_Reg test_lib[] = {
201  		{ "exec", lua_exec },
202  		{ NULL, NULL },
203  	};
204  	luaL_openlibs(L);
205  	lua_override_ios(L, false);
206  	luaL_newlib(L, test_lib);
207  	lua_setglobal(L, "test");
208  
209  	pid_t p = atf_utils_fork();
210  	if (p == 0) {
211  		if (luaL_dostring(L, "test.exec()")) {
212  			printf("%s\n", lua_tostring(L, -1));
213  		}
214  		exit(lua_tonumber(L, -1));
215  	}
216  	atf_utils_wait(p, 0, "[string \"test.exec()\"]:1: bad argument #0 to 'exec' (pkg.exec takes exactly one argument)\n", "");
217  
218  	p = atf_utils_fork();
219  	if (p == 0) {
220  		if (luaL_dostring(L, "test.exec(plop)")) {
221  			printf("%s\n", lua_tostring(L, -1));
222  		}
223  		exit(lua_tonumber(L, -1));
224  	}
225  	atf_utils_wait(p, 0, "[string \"test.exec(plop)\"]:1: bad argument #1 to 'exec' (table expected, got nil)\n", "");
226  
227  	p = atf_utils_fork();
228  	if (p == 0) {
229  		if (luaL_dostring(L, "test.exec(plop, meh)")) {
230  			printf("%s\n", lua_tostring(L, -1));
231  		}
232  		exit(lua_tonumber(L, -1));
233  	}
234  	atf_utils_wait(p, 0, "[string \"test.exec(plop, meh)\"]:1: bad argument #2 to 'exec' (pkg.exec takes exactly one argument)\n", "");
235  
236  	p = atf_utils_fork();
237  	if (p == 0) {
238  		if (luaL_dostring(L, "test.exec({\"/bin/echo\", \"1\"})")) {
239  			printf("%s\n", lua_tostring(L, -1));
240  		}
241  		exit(lua_tonumber(L, -1));
242  	}
243  	atf_utils_wait(p, 0, "1\n", "");
244  }
245  
246  ATTRIBUTE_NO_SANITIZE_ADDRESS ATF_TC_BODY(override, tc)
247  {
248  	lua_State *L = luaL_newstate();
249  	luaL_openlibs(L);
250  	lua_override_ios(L, true);
251  
252  	pid_t p = atf_utils_fork();
253  	if (p == 0) {
254  		if (luaL_dostring(L, "os.execute(\"/usr/bin/true\")")) {
255  			printf("%s\n", lua_tostring(L, -1));
256  		}
257  		exit(lua_tonumber(L, -1));
258  	}
259  	atf_utils_wait(p, 0, "[string \"os.execute(\"/usr/bin/true\")\"]:1: os.execute not available\n", "");
260  
261  	p = atf_utils_fork();
262  	if (p == 0) {
263  		if (luaL_dostring(L, "os.exit(1)")) {
264  			printf("%s\n", lua_tostring(L, -1));
265  		}
266  		exit(lua_tonumber(L, -1));
267  	}
268  	atf_utils_wait(p, 0, "[string \"os.exit(1)\"]:1: os.exit not available\n", "");
269  
270  	char *cwd = getcwd(NULL, 0);
271  	int rootfd = open(cwd, O_DIRECTORY);
272  	free(cwd);
273  	lua_pushinteger(L, rootfd);
274  	lua_setglobal(L, "rootfd");
275  	p = atf_utils_fork();
276  	if (p == 0) {
277  		if (luaL_dostring(L, "io.close(io.open(\"/plop\", \"w+\"))")) {
278  			printf("%s\n", lua_tostring(L, -1));
279  		}
280  		exit(lua_tonumber(L, -1));
281  	}
282  	atf_utils_wait(p, 0, "", "");
283  	atf_utils_file_exists("plop");
284  
285  	p = atf_utils_fork();
286  	if (p == 0) {
287  		if (luaL_dostring(L, "os.rename(\"/plop\", \"/bob\")")) {
288  			printf("%s\n", lua_tostring(L, -1));
289  		}
290  		exit(lua_tonumber(L, -1));
291  	}
292  	atf_utils_wait(p, 0, "", "");
293  	atf_utils_file_exists("bob");
294  
295  	p = atf_utils_fork();
296  	if (p == 0) {
297  		if (luaL_dostring(L, "os.remove(\"/bob\")\nassert(io.open(\"/bob\", \"r\"))")) {
298  			printf("%s\n", lua_tostring(L, -1));
299  		}
300  		exit(lua_tonumber(L, -1));
301  	}
302  	atf_utils_wait(p, 0, "[string \"os.remove(\"/bob\")...\"]:2: /bob: No such file or directory\n", "");
303  }
304  
305  ATF_TC_BODY(fileops, tc)
306  {
307  	char b[1024];
308  	char *cwd = getcwd(NULL, 0);
309  	int rootfd = open(cwd, O_DIRECTORY);
310  	free(cwd);
311  	lua_State *L = luaL_newstate();
312  	luaL_openlibs(L);
313  	lua_pushinteger(L, rootfd);
314  	lua_setglobal(L, "rootfd");
315  	lua_override_ios(L, true);
316  	static const luaL_Reg test_lib[] = {
317  		{ "copy", lua_pkg_copy },
318  		{ "cmp", lua_pkg_filecmp},
319  		{ "symlink", lua_pkg_symlink},
320  		{ NULL, NULL },
321  	};
322  	luaL_newlib(L, test_lib);
323  	lua_setglobal(L, "test");
324  	pid_t p;
325  
326  	FILE *f1 = fopen("test1", "w+");
327  	FILE *f2 = fopen("test2", "w+");
328  	FILE *f3 = fopen("test3", "w+");
329  
330  	fputs("test", f1);
331  	fputs("test2", f2);
332  	fputs("test", f3);
333  	fclose(f1);
334  	fclose(f2);
335  	fclose(f3);
336  
337  	p = atf_utils_fork();
338  	if (p == 0) {
339  		if (luaL_dostring(L, "test.cmp(1)")) {
340  			printf("%s\n", lua_tostring(L, -1));
341  		}
342  		exit(lua_tonumber(L, -1));
343  	}
344  	atf_utils_wait(p, 0, "[string \"test.cmp(1)\"]:1: bad argument #1 to 'cmp' (pkg.filecmp takes exactly two arguments)\n", "");
345  
346  	p = atf_utils_fork();
347  	if (p == 0) {
348  		if (luaL_dostring(L, "test.cmp(1, 2, 3)")) {
349  			printf("%s\n", lua_tostring(L, -1));
350  		}
351  		exit(lua_tonumber(L, -1));
352  	}
353  	atf_utils_wait(p, 0, "[string \"test.cmp(1, 2, 3)\"]:1: bad argument #3 to 'cmp' (pkg.filecmp takes exactly two arguments)\n", "");
354  
355  	p = atf_utils_fork();
356  	if (p == 0) {
357  		if (luaL_dostring(L, "return test.cmp(1, 2)")) {
358  			printf("%s\n", lua_tostring(L, -1));
359  		}
360  		exit(lua_tonumber(L, -1));
361  	}
362  	atf_utils_wait(p, 2, "", "");
363  
364  	p = atf_utils_fork();
365  	if (p == 0) {
366  		if (luaL_dostring(L, "return test.cmp(\"test1\", 2)")) {
367  			printf("%s\n", lua_tostring(L, -1));
368  		}
369  		exit(lua_tonumber(L, -1));
370  	}
371  	atf_utils_wait(p, 2, "", "");
372  
373  	p = atf_utils_fork();
374  	if (p == 0) {
375  		if (luaL_dostring(L, "return test.cmp(\"test1\", \"test2\")")) {
376  			printf("%s\n", lua_tostring(L, -1));
377  		}
378  		exit(lua_tonumber(L, -1));
379  	}
380  	atf_utils_wait(p, 1, "", "");
381  
382  	p = atf_utils_fork();
383  	if (p == 0) {
384  		if (luaL_dostring(L, "return test.cmp(\"test1\", \"test3\")")) {
385  			printf("%s\n", lua_tostring(L, -1));
386  		}
387  		exit(lua_tonumber(L, -1));
388  	}
389  	atf_utils_wait(p, 0, "", "");
390  
391  	p = atf_utils_fork();
392  	if (p == 0) {
393  		if (luaL_dostring(L, "return(test.copy(1, 2))")) {
394  			printf("%s\n", lua_tostring(L, -1));
395  		}
396  		exit(lua_tonumber(L, -1));
397  	}
398  	atf_utils_wait(p, 2, "", "");
399  
400  	p = atf_utils_fork();
401  	if (p == 0) {
402  		if (luaL_dostring(L, "return(test.copy(\"test1\", \"nonexistent/2\"))")) {
403  			printf("%s\n", lua_tostring(L, -1));
404  		}
405  		exit(lua_tonumber(L, -1));
406  	}
407  	atf_utils_wait(p, 2, "", "");
408  
409  	p = atf_utils_fork();
410  	if (p == 0) {
411  		if (luaL_dostring(L, "test.copy(\"test1\", \"test4\")\nreturn test.cmp(\"test1\", \"test4\")")) {
412  			printf("%s\n", lua_tostring(L, -1));
413  		}
414  		exit(lua_tonumber(L, -1));
415  	}
416  	atf_utils_wait(p, 0, "", "");
417  
418  	p = atf_utils_fork();
419  	if (p == 0) {
420  		if (luaL_dostring(L, "test.symlink(\"a\", \"b\", \"meh\")\n")) {
421  			printf("%s\n", lua_tostring(L, -1));
422  		}
423  		exit(lua_tonumber(L, -1));
424  	}
425  	atf_utils_wait(p, 0, "[string \"test.symlink(\"a\", \"b\", \"meh\")...\"]:1: bad argument #3 to 'symlink' (pkg.symlink takes exactly two arguments)\n", "");
426  
427  	p = atf_utils_fork();
428  	if (p == 0) {
429  		if (luaL_dostring(L, "test.symlink(\"a\")\n")) {
430  			printf("%s\n", lua_tostring(L, -1));
431  		}
432  		exit(lua_tonumber(L, -1));
433  	}
434  	atf_utils_wait(p, 0, "[string \"test.symlink(\"a\")...\"]:1: bad argument #1 to 'symlink' (pkg.symlink takes exactly two arguments)\n", "");
435  
436  	p = atf_utils_fork();
437  	if (p == 0) {
438  		if (luaL_dostring(L, "test.symlink(\"a\", \"b\")\n")) {
439  			printf("%s\n", lua_tostring(L, -1));
440  		}
441  		exit(lua_tonumber(L, -1));
442  	}
443  	atf_utils_wait(p, 0, "", "");
444  	struct stat st;
445  	if (lstat("b", &st) != 0)
446  		atf_tc_fail("File 'b' not created");
447  	if (!S_ISLNK(st.st_mode))
448  		atf_tc_fail("File 'b' is not a symlink");
449  	memset(b, 0, sizeof(b));
450  	readlink("b", b, sizeof(b));
451  	ATF_REQUIRE_STREQ(b, "a");
452  
453  }
454  
455  ATF_TC_BODY(prefix_path, tc)
456  {
457  	struct pkg *pkg = NULL;
458  	pkg_new(&pkg, PKG_INSTALLED);
459  	pkg_set(pkg, PKG_ATTR_PREFIX, "/myprefix");
460  	lua_State *L = luaL_newstate();
461  	static const luaL_Reg test_lib[] = {
462  		{ "prefix_path", lua_prefix_path },
463  		{ NULL, NULL },
464  	};
465  	luaL_openlibs(L);
466  	lua_override_ios(L, false);
467  	luaL_newlib(L, test_lib);
468  	lua_setglobal(L, "test");
469  	lua_pushlightuserdata(L, pkg);
470  	lua_setglobal(L, "package");
471  	pid_t p;
472  
473  	p = atf_utils_fork();
474  	if (p == 0) {
475  		if (luaL_dostring(L, "print(test.prefix_path())")) {
476  			printf("%s\n", lua_tostring(L, -1));
477  		}
478  		exit(lua_tonumber(L, -1));
479  	}
480  	atf_utils_wait(p, 0, "[string \"print(test.prefix_path())\"]:1: bad argument #0 to 'prefix_path' (pkg.prefix_path takes exactly one argument)\n", "");
481  
482  	p = atf_utils_fork();
483  	if (p == 0) {
484  		if (luaL_dostring(L, "print(test.prefix_path(1, 2))")) {
485  			printf("%s\n", lua_tostring(L, -1));
486  		}
487  		exit(lua_tonumber(L, -1));
488  	}
489  	atf_utils_wait(p, 0, "[string \"print(test.prefix_path(1, 2))\"]:1: bad argument #2 to 'prefix_path' (pkg.prefix_path takes exactly one argument)\n", "");
490  
491  	p = atf_utils_fork();
492  	if (p == 0) {
493  		if (luaL_dostring(L, "print(test.prefix_path(1))")) {
494  			printf("%s\n", lua_tostring(L, -1));
495  		}
496  		exit(lua_tonumber(L, -1));
497  	}
498  	atf_utils_wait(p, 0, "/myprefix/1\n", "");
499  
500  	p = atf_utils_fork();
501  	if (p == 0) {
502  		if (luaL_dostring(L, "print(test.prefix_path(\"/1\"))")) {
503  			printf("%s\n", lua_tostring(L, -1));
504  		}
505  		exit(lua_tonumber(L, -1));
506  	}
507  	atf_utils_wait(p, 0, "/1\n", "");
508  }
509  
510  ATF_TP_ADD_TCS(tp)
511  {
512  	ATF_TP_ADD_TC(tp, readdir);
513  	ATF_TP_ADD_TC(tp, stat);
514  	ATF_TP_ADD_TC(tp, print_msg);
515  	ATF_TP_ADD_TC(tp, execute);
516  	ATF_TP_ADD_TC(tp, override);
517  	ATF_TP_ADD_TC(tp, fileops);
518  	ATF_TP_ADD_TC(tp, prefix_path);
519  
520  	return (atf_no_error());
521  }