File Coverage

Restrict.xs
Criterion Covered Total %
statement 183 195 93.8
branch 282 642 43.9
condition n/a
subroutine n/a
pod n/a
total 465 837 55.5


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2             #include "easyxs/easyxs.h"
3              
4             // Cribbed from Socket.xs:
5             /* STRUCT_OFFSET should have come from from perl.h, but if not,
6             * roll our own (not using offsetof() since that is C99). */
7             #ifndef STRUCT_OFFSET
8             # define STRUCT_OFFSET(s,m) (Size_t)(&(((s *)0)->m))
9             #endif
10              
11             #define STRUCT_MEMBER_SIZE(s,m) sizeof(((s *)0)->m)
12              
13             // Modern Win32 releases do support UNIX sockets via afunix.h,
14             // but that header file isn’t available on MinGW. Socket.xs defines
15             // the necessary structs and constants directly; if needed we could
16             // take that approach, but for now let’s just forgo UNIX socket support
17             // unless there’s sys/un.h.
18             #ifdef I_SYS_UN // cf. perl5 Porting/Glossary
19              
20             #define HAS_UNIX_SOCKETS 1
21             #include
22              
23             #define SA_FAMILY_END_OFFSET ( \
24             STRUCT_OFFSET(struct sockaddr, sa_family) \
25             + STRUCT_MEMBER_SIZE(struct sockaddr, sa_family) \
26             )
27              
28             #else
29             #define HAS_UNIX_SOCKETS 0
30             #endif
31              
32             #include
33             #include
34              
35             #include "ppport.h"
36              
37             #define DEBUG 0
38              
39             /* A duplicate of PL_ppaddr as we find it at BOOT time.
40             We can thus overwrite PL_ppaddr with our own wrapper functions.
41             This interacts better with wrap_op_checker(), which doesn’t provide
42             a good way to call the op’s (now-overwritten) op_ppaddr callback.
43             */
44             static Perl_ppaddr_t ORIG_PL_ppaddr[OP_max];
45              
46             #define MYPKG "Filesys::Restrict"
47              
48             /* An idempotent variant of dMARK that allows us to inspect the
49             mark stack without changing it: */
50             #ifndef dMARK_TOPMARK
51             #define dMARK_TOPMARK SV **mark = PL_stack_base + TOPMARK
52             #endif
53              
54 160           static inline SV* _get_callback(pTHX) {
55 160           SV* callback = get_sv(MYPKG "::_AUTHORIZE", 0);
56              
57 160 50         if (callback && !SvOK(callback)) {
    100          
    50          
    50          
58 5           callback = NULL;
59             }
60              
61 160           return callback;
62             }
63              
64             #define _IS_FILEHANDLE(expr) ( \
65             (SvTYPE(expr) == SVt_PVGV) || \
66             (SvROK(expr) && SvTYPE(SvRV(expr)) == SVt_PVGV) || \
67             (SvTYPE(expr) == SVt_PVIO) || \
68             (SvROK(expr) && SvTYPE(SvRV(expr)) == SVt_PVIO) \
69             )
70              
71 25           bool _is_pipe_open(pTHX_ const char* str, STRLEN len) {
72 25 100         if (len >= 2) {
73 17 50         if (memEQ("|-", str, 2)) return true;
74 17 50         if (memEQ("-|", str, 2)) return true;
75             }
76              
77 25           return false;
78             }
79              
80             #define _IS_SCALAR_REF(sv) (SvROK(sv) && (SvTYPE(SvRV(sv)) < SVt_PVGV))
81              
82             // Returns NULL to indicate no path.
83 13           static SV* _get_path_from_3arg_open(pTHX_ SV* mode, SV* expr) {
84              
85             // 3-arg open with undef 3rd arg opens an anonymous tempfile.
86 13 100         if (!SvOK(expr)) return NULL;
    50          
    50          
87              
88             // Ignore scalar-reference paths, which indicate an “open”
89             // of a Perl scalar.
90 12 100         if (_IS_SCALAR_REF(expr)) return NULL;
    50          
91              
92 12 50         if (!SvPOK(mode)) croak("mode isn’t a string?!?");
93              
94             STRLEN modelen;
95 12 50         const char* modestr = SvPVbyte(mode, modelen);
96              
97 12 50         if (_is_pipe_open(aTHX_ modestr, modelen)) return NULL;
98              
99             // If the last character of the mode is '=' then expr is a
100             // file descriptor or filehandle, so we shouldn’t care.
101 12 100         if (NULL != strchr(modestr, '&')) return NULL;
102              
103 13           return expr;
104             }
105              
106 13           static SV* _get_path_from_2arg_open(pTHX_ SV* expr) {
107             STRLEN len;
108 13 50         const char* str = SvPVbyte(expr, len);
109              
110 13 50         if (_is_pipe_open(aTHX_ str, len)) return NULL;
111              
112 13 50         if (len < 1) return NULL;
113              
114             // Special cases:
115 13 50         if (len == 2) {
116 0 0         if (memEQ(str, ">-", 2)) return NULL; // opens STDOUT
117 0 0         if (memEQ(str, "<-", 2)) return NULL; // opens STDIN
118             }
119              
120 13           STRLEN idx = 0;
121              
122 13 100         if (str[idx] == '+') idx++;
123              
124 13 100         if (str[idx] == '<') {
125 5           idx++;
126             }
127 8 50         else if (str[idx] == '>') {
128 8           idx++;
129              
130 8 100         if (str[idx] == '>') idx++;
131             }
132             else {
133              
134             // For now ignore anything that isn’t +>, +>>, +<, >, >>, or <.
135 0           return NULL;
136             }
137              
138             // Duplicating a filehandle or file descriptor is always OK.
139 13 50         if (str[idx] == '&') return NULL;
140              
141 13           return newSVpvn_flags(
142             str + idx,
143             len - idx,
144             SVs_TEMP
145             );
146             }
147              
148 194           static inline void _prep_stack(pTHX_ SV** args, unsigned argscount) {
149 194           dSP;
150              
151 194           ENTER;
152 194           SAVETMPS;
153              
154 194 50         PUSHMARK(SP);
155 194 50         EXTEND(SP, argscount);
156              
157             unsigned a;
158              
159 582 100         for (a=0; a < argscount; a++) PUSHs(args[a]);
160              
161 194           PUTBACK;
162 194           }
163              
164 137           static inline void _authorize(pTHX_ int OPID, SV* path_sv, SV* callback_sv) {
165 137           const char* opname = PL_op_desc[OPID];
166              
167 137           dSP;
168              
169 137           SV* args[] = {
170 137           newSVpvn_flags(opname, strlen(opname), SVs_TEMP),
171             path_sv,
172             };
173              
174 137           _prep_stack(aTHX_ args, 2);
175              
176 137           I32 returns = call_sv( callback_sv, G_SCALAR );
177              
178 137           SPAGAIN;
179              
180             bool authorized;
181              
182 137 50         if (returns) {
183 137           SV* got = POPs;
184 137 50         authorized = SvTRUE(got);
    50          
    0          
    50          
    0          
    0          
    100          
    50          
    50          
    100          
    50          
    100          
    50          
    50          
    50          
    0          
    0          
    50          
    0          
185             }
186             else {
187 0           authorized = false;
188             }
189              
190 137           PUTBACK;
191 137 50         FREETMPS;
192 137           LEAVE;
193              
194 137 100         if (!authorized) {
195 57           _prep_stack(aTHX_ args, 2);
196              
197 57           call_pv( MYPKG "::_CROAK", G_VOID | G_DISCARD );
198              
199             // We should never get here:
200             assert(0);
201             }
202 80           }
203              
204             // open() is such a funny beast that it gets its own wrapper.
205 26           static OP* _wrapped_pp_OP_OPEN(pTHX) {
206 26           SV* callback = _get_callback(aTHX);
207 26 50         if (callback) {
208 26           dSP;
209 26           dMARK_TOPMARK;
210              
211 26           int numargs = SP - MARK;
212              
213             SV* path;
214              
215 26           switch (numargs) {
216             case 1:
217 0           path = NULL;
218 0           break;
219              
220             case 2:
221 13           path = _get_path_from_2arg_open(aTHX_ MARK[2]);
222 13           break;
223              
224             case 3:
225 13           path = _get_path_from_3arg_open(aTHX_ MARK[2], MARK[3]);
226              
227 13           break;
228              
229             default:
230              
231             // Shouldn’t happen, but just in case …
232 0           croak("Bad # of args: %d", numargs);
233             }
234              
235 26 100         if (path) {
236 21           _authorize(aTHX_ OP_OPEN, path, callback);
237             }
238             }
239              
240 17           return ORIG_PL_ppaddr[OP_OPEN](aTHX);
241             }
242              
243             # if 0
244             // require() also gets its own wrapper since it depends on @INC.
245             static OP* _wrapped_pp_OP_REQUIRE(pTHX) {
246             SV* callback = _get_callback(aTHX);
247             if (callback) {
248             dSP;
249              
250             SV* required = *SP;
251              
252             STRLEN reqlen;
253             const char* required_str = SvPVbyte(required, reqlen);
254              
255             if (reqlen && NULL != strchr("./", required_str[0])) {
256             _authorize(aTHX_ OP_REQUIRE, required, callback);
257             }
258             }
259              
260             return ORIG_PL_ppaddr[OP_REQUIRE](aTHX);
261             }
262             #endif
263              
264             #define MAKE_LAST_ARG_WRAPPER(OPID, CHECK_FH) \
265             static OP* _wrapped_pp_##OPID(pTHX) { \
266             SV* callback = _get_callback(aTHX); \
267             if (callback) { \
268             dSP; \
269             if (!CHECK_FH || !_IS_FILEHANDLE(*SP)) { \
270             _authorize(aTHX_ OPID, *SP, callback); \
271             } \
272             } \
273             \
274             return ORIG_PL_ppaddr[OPID](aTHX); \
275             }
276              
277             #define MAKE_LAST_ARG_WRAPPER_CHECK_FH(OPID) \
278             MAKE_LAST_ARG_WRAPPER(OPID, 1)
279              
280             #define MAKE_LAST_ARG_WRAPPER_NO_CHECK_FH(OPID) \
281             MAKE_LAST_ARG_WRAPPER(OPID, 0)
282              
283             #define MAKE_2ARG_WRAPPER(OPID) \
284             static OP* _wrapped_pp_##OPID(pTHX) { \
285             SV* callback = _get_callback(aTHX); \
286             if (callback) { \
287             dSP; \
288             _authorize(aTHX_ OPID, *SP, callback); \
289             _authorize(aTHX_ OPID, *(SP - 1), callback); \
290             } \
291             \
292             return ORIG_PL_ppaddr[OPID](aTHX); \
293             }
294              
295             // ----------------------------------------------------------------------
296              
297             #define _MY_SET_SP_AND_MARK(OP_MAXARG) \
298             dSP; \
299             dMARK_TOPMARK; \
300             \
301             /* The compiler will optimize this away \
302             for MAKE_FIRST_ARG_OPEN_LIST_WRAPPER: \
303             */ \
304             if (OP_MAXARG) \
305             if (SP < MARK || (SP - MARK) > OP_MAXARG) { \
306             unsigned numargs = MAXARG; \
307             MARK = SP; \
308             while (numargs--) MARK--; \
309             }
310              
311             /* For ops that take an indefinite number of args. */
312             #define MAKE_FIRST_ARG_OPEN_LIST_WRAPPER(OPID) \
313             MAKE_SINGLE_ARG_LIST_WRAPPER(OPID, 0, 0)
314              
315             /* For ops whose number of string args is a fixed range.
316              
317             NB: In some perls, some list opts don’t set MARK. In those cases we
318             fall back to MAXARG. As of now mkdir is the known “offender”, and
319             only on Alpine Linux 3.11 & 3.12 (not 3.13).
320             */
321             #define MAKE_SINGLE_ARG_LIST_WRAPPER(OPID, ARG_INDEX, OP_MAXARG) \
322             static OP* _wrapped_pp_##OPID(pTHX) { \
323             SV* callback = _get_callback(aTHX); \
324             if (callback) { \
325             _MY_SET_SP_AND_MARK(OP_MAXARG) \
326             \
327             _authorize(aTHX_ OPID, MARK[1 + ARG_INDEX], callback); \
328             } \
329             \
330             return ORIG_PL_ppaddr[OPID](aTHX); \
331             }
332              
333 3           static OP* _wrapped_pp_OP_TRUNCATE(pTHX) {
334 3           SV* callback = _get_callback(aTHX);
335 3 50         if (callback) {
336 3           dSP;
337 3           SV* first_arg = *(SP - 1);
338              
339 3 50         if (!_IS_FILEHANDLE(first_arg)) {
    100          
    50          
    50          
    50          
    0          
340 2           _authorize(aTHX_ OP_TRUNCATE, first_arg, callback);
341             }
342             }
343              
344 2           return ORIG_PL_ppaddr[OP_TRUNCATE](aTHX);
345             }
346              
347             #define MAKE_ALL_ARGS_LIST_WRAPPER_CHECK_FH(OPID, ARG_INDEX) \
348             static OP* _wrapped_pp_##OPID(pTHX) { \
349             SV* callback = _get_callback(aTHX); \
350             if (callback) { \
351             _MY_SET_SP_AND_MARK(0) \
352             \
353             MARK += ARG_INDEX; \
354             while (++MARK <= SP) { \
355             if (!_IS_FILEHANDLE(*MARK)) { \
356             _authorize(aTHX_ OPID, *MARK, callback); \
357             } \
358             } \
359             } \
360             \
361             return ORIG_PL_ppaddr[OPID](aTHX); \
362             }
363              
364             /* For ops where only the last arg is a string. */
365             #define MAKE_SOCKET_OP_WRAPPER(OPID) \
366             static OP* _wrapped_pp_##OPID(pTHX) { \
367             SV* callback = _get_callback(aTHX); \
368             if (callback) { \
369             dSP; \
370             STRLEN pathlen; \
371             const char* path = _get_local_socket_path(aTHX_ SP[0], &pathlen); \
372             if (path) { \
373             SV* path_sv = newSVpvn_flags(path, pathlen, SVs_TEMP); \
374             _authorize(aTHX_ OPID, path_sv, callback); \
375             } \
376             } \
377             \
378             return ORIG_PL_ppaddr[OPID](aTHX); \
379             }
380              
381 5           const char* _get_local_socket_path(pTHX_ SV* sockname_sv, STRLEN *pathlen) {
382 5           char* path = NULL;
383              
384             #if HAS_UNIX_SOCKETS
385             STRLEN sockname_len;
386 5 50         const char* sockname_str = SvPVbyte(sockname_sv, sockname_len);
387              
388             // Let Perl handle the failure state:
389 5 50         if (sockname_len >= SA_FAMILY_END_OFFSET) {
390 5           sa_family_t family = ( (struct sockaddr*) sockname_str )->sa_family;
391              
392 5 50         if (family == AF_UNIX) {
393 5           path = ( (struct sockaddr_un*) sockname_str )->sun_path;
394 5           *pathlen = sockname_len - STRUCT_OFFSET(struct sockaddr_un, sun_path);
395             }
396             }
397             #endif
398              
399 5           return path;
400             }
401              
402 5 50         MAKE_SOCKET_OP_WRAPPER(OP_BIND);
    50          
403 3 50         MAKE_SOCKET_OP_WRAPPER(OP_CONNECT);
    50          
404              
405 6 50         MAKE_SINGLE_ARG_LIST_WRAPPER(OP_SYSOPEN, 1, 4);
    50          
    50          
    0          
406              
407 0 0         MAKE_FIRST_ARG_OPEN_LIST_WRAPPER(OP_EXEC);
408 6 50         MAKE_FIRST_ARG_OPEN_LIST_WRAPPER(OP_SYSTEM);
409              
410 0 0         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_LSTAT);
    0          
    0          
    0          
    0          
    0          
    0          
411 2 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_STAT);
    50          
    50          
    0          
    50          
    50          
    0          
412 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTRREAD);
    100          
    50          
    0          
    50          
    50          
    0          
413 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTRWRITE);
    100          
    50          
    0          
    50          
    50          
    0          
414 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTREXEC);
    100          
    50          
    0          
    50          
    50          
    0          
415 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTEREAD);
    100          
    50          
    0          
    50          
    50          
    0          
416 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTEWRITE);
    100          
    50          
    0          
    50          
    50          
    0          
417 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTEEXEC);
    100          
    50          
    0          
    50          
    50          
    0          
418 7 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTIS);
    100          
    50          
    0          
    50          
    50          
    0          
419 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTSIZE);
    100          
    50          
    0          
    50          
    50          
    0          
420 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTMTIME);
    100          
    50          
    0          
    50          
    50          
    0          
421 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTATIME);
    100          
    50          
    0          
    50          
    50          
    0          
422 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTCTIME);
    100          
    50          
    0          
    50          
    50          
    0          
423 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTROWNED);
    100          
    50          
    0          
    50          
    50          
    0          
424 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTEOWNED);
    100          
    50          
    0          
    50          
    50          
    0          
425 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTZERO);
    100          
    50          
    0          
    50          
    50          
    0          
426 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTSOCK);
    100          
    50          
    0          
    50          
    50          
    0          
427 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTCHR);
    100          
    50          
    0          
    50          
    50          
    0          
428 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTBLK);
    100          
    50          
    0          
    50          
    50          
    0          
429 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTFILE);
    100          
    50          
    0          
    50          
    50          
    0          
430 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTDIR);
    100          
    50          
    0          
    50          
    50          
    0          
431 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTPIPE);
    100          
    50          
    0          
    50          
    50          
    0          
432 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTSUID);
    100          
    50          
    0          
    50          
    50          
    0          
433 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTSGID);
    100          
    50          
    0          
    50          
    50          
    0          
434 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTSVTX);
    100          
    50          
    0          
    50          
    50          
    0          
435 3 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTLINK);
    50          
    50          
    0          
    50          
    50          
    0          
436 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTTEXT);
    100          
    50          
    0          
    50          
    50          
    0          
437 5 50         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_FTBINARY);
    100          
    50          
    0          
    50          
    50          
    0          
438 0 0         MAKE_LAST_ARG_WRAPPER_CHECK_FH(OP_CHDIR);
    0          
    0          
    0          
    0          
    0          
    0          
439 13 50         MAKE_ALL_ARGS_LIST_WRAPPER_CHECK_FH(OP_CHOWN, 2);
    50          
    100          
    50          
    50          
    50          
    0          
    100          
440 0 0         MAKE_LAST_ARG_WRAPPER_NO_CHECK_FH(OP_CHROOT);
441 10 50         MAKE_ALL_ARGS_LIST_WRAPPER_CHECK_FH(OP_UNLINK, 0);
    50          
    50          
    0          
    50          
    50          
    0          
    100          
442 16 50         MAKE_ALL_ARGS_LIST_WRAPPER_CHECK_FH(OP_CHMOD, 1);
    50          
    100          
    50          
    50          
    50          
    0          
    100          
443 13 50         MAKE_ALL_ARGS_LIST_WRAPPER_CHECK_FH(OP_UTIME, 2);
    50          
    100          
    50          
    50          
    50          
    0          
    100          
444 5 50         MAKE_2ARG_WRAPPER(OP_RENAME);
445 5 50         MAKE_2ARG_WRAPPER(OP_LINK);
446              
447             // symlink() is special: its “target” (the first arg) and doesn’t
448             // need to refer to a valid filesystem node.
449 0 0         MAKE_LAST_ARG_WRAPPER_NO_CHECK_FH(OP_SYMLINK);
450              
451 3 50         MAKE_LAST_ARG_WRAPPER_NO_CHECK_FH(OP_READLINK);
452 16 100         MAKE_SINGLE_ARG_LIST_WRAPPER(OP_MKDIR, 0, 2);
    50          
    50          
    0          
453 3 50         MAKE_LAST_ARG_WRAPPER_NO_CHECK_FH(OP_RMDIR);
454 3 50         MAKE_LAST_ARG_WRAPPER_NO_CHECK_FH(OP_OPEN_DIR);
455              
456             // MAKE_LAST_ARG_WRAPPER_NO_CHECK_FH(OP_DOFILE);
457              
458             /* ---------------------------------------------------------------------- */
459              
460             #define MAKE_BOOT_WRAPPER(OPID) \
461             ORIG_PL_ppaddr[OPID] = PL_ppaddr[OPID]; \
462             PL_ppaddr[OPID] = _wrapped_pp_##OPID;
463              
464             //----------------------------------------------------------------------
465              
466             bool initialized = false;
467              
468             MODULE = Filesys::Restrict PACKAGE = Filesys::Restrict
469              
470             PROTOTYPES: DISABLE
471              
472             BOOT:
473             /* In theory this is for PL_check rather than PL_ppaddr, but per
474             Paul Evans in practice this mutex gets used for other stuff, too.
475             Paul says a race here should be exceptionally rare, so for pre-5.16
476             perls (which lack this mutex) let’s just skip it.
477             */
478             #ifdef OP_CHECK_MUTEX_LOCK
479             OP_CHECK_MUTEX_LOCK;
480             #endif
481 6 50         if (!initialized) {
482 6           initialized = true;
483              
484 6           MAKE_BOOT_WRAPPER(OP_OPEN);
485 6           MAKE_BOOT_WRAPPER(OP_SYSOPEN);
486 6           MAKE_BOOT_WRAPPER(OP_TRUNCATE);
487 6           MAKE_BOOT_WRAPPER(OP_EXEC);
488 6           MAKE_BOOT_WRAPPER(OP_SYSTEM);
489              
490             if (HAS_UNIX_SOCKETS) {
491 6           MAKE_BOOT_WRAPPER(OP_BIND);
492 6           MAKE_BOOT_WRAPPER(OP_CONNECT);
493             }
494              
495 6           HV *stash = gv_stashpv(MYPKG, FALSE);
496 6           newCONSTSUB(stash, "_HAS_UNIX_SOCKETS", boolSV(HAS_UNIX_SOCKETS));
497              
498 6           MAKE_BOOT_WRAPPER(OP_LSTAT);
499 6           MAKE_BOOT_WRAPPER(OP_STAT);
500 6           MAKE_BOOT_WRAPPER(OP_FTRREAD);
501 6           MAKE_BOOT_WRAPPER(OP_FTRWRITE);
502 6           MAKE_BOOT_WRAPPER(OP_FTREXEC);
503 6           MAKE_BOOT_WRAPPER(OP_FTEREAD);
504 6           MAKE_BOOT_WRAPPER(OP_FTEWRITE);
505 6           MAKE_BOOT_WRAPPER(OP_FTEEXEC);
506 6           MAKE_BOOT_WRAPPER(OP_FTIS);
507 6           MAKE_BOOT_WRAPPER(OP_FTSIZE);
508 6           MAKE_BOOT_WRAPPER(OP_FTMTIME);
509 6           MAKE_BOOT_WRAPPER(OP_FTATIME);
510 6           MAKE_BOOT_WRAPPER(OP_FTCTIME);
511 6           MAKE_BOOT_WRAPPER(OP_FTROWNED);
512 6           MAKE_BOOT_WRAPPER(OP_FTEOWNED);
513 6           MAKE_BOOT_WRAPPER(OP_FTZERO);
514 6           MAKE_BOOT_WRAPPER(OP_FTSOCK);
515 6           MAKE_BOOT_WRAPPER(OP_FTCHR);
516 6           MAKE_BOOT_WRAPPER(OP_FTBLK);
517 6           MAKE_BOOT_WRAPPER(OP_FTFILE);
518 6           MAKE_BOOT_WRAPPER(OP_FTDIR);
519 6           MAKE_BOOT_WRAPPER(OP_FTPIPE);
520 6           MAKE_BOOT_WRAPPER(OP_FTSUID);
521 6           MAKE_BOOT_WRAPPER(OP_FTSGID);
522 6           MAKE_BOOT_WRAPPER(OP_FTSVTX);
523 6           MAKE_BOOT_WRAPPER(OP_FTLINK);
524 6           MAKE_BOOT_WRAPPER(OP_FTTEXT);
525 6           MAKE_BOOT_WRAPPER(OP_FTBINARY);
526 6           MAKE_BOOT_WRAPPER(OP_CHDIR);
527 6           MAKE_BOOT_WRAPPER(OP_CHOWN);
528 6           MAKE_BOOT_WRAPPER(OP_CHROOT);
529 6           MAKE_BOOT_WRAPPER(OP_UNLINK);
530 6           MAKE_BOOT_WRAPPER(OP_CHMOD);
531 6           MAKE_BOOT_WRAPPER(OP_UTIME);
532 6           MAKE_BOOT_WRAPPER(OP_RENAME);
533 6           MAKE_BOOT_WRAPPER(OP_LINK);
534 6           MAKE_BOOT_WRAPPER(OP_SYMLINK);
535 6           MAKE_BOOT_WRAPPER(OP_READLINK);
536 6           MAKE_BOOT_WRAPPER(OP_MKDIR);
537 6           MAKE_BOOT_WRAPPER(OP_RMDIR);
538 6           MAKE_BOOT_WRAPPER(OP_OPEN_DIR);
539              
540             // MAKE_BOOT_WRAPPER(OP_REQUIRE);
541             // MAKE_BOOT_WRAPPER(OP_DOFILE);
542             }
543             #ifdef OP_CHECK_MUTEX_UNLOCK
544             OP_CHECK_MUTEX_UNLOCK;
545             #endif