source: branches/gsoc13-tests/src/darwintracelib1.0/darwintrace.c @ 111323

Last change on this file since 111323 was 111323, checked in by marius@…, 7 years ago

Merge from trunk.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 41.2 KB
Line 
1/*
2 * Copyright (c) 2005 Apple Inc. All rights reserved.
3 * Copyright (c) 2005-2006 Paul Guyot <pguyot@kallisys.net>,
4 * All rights reserved.
5 * Copyright (c) 2006-2013 The MacPorts Project
6 *
7 * $Id: darwintrace.c 111323 2013-09-18 23:11:02Z marius@macports.org $
8 *
9 * @APPLE_BSD_LICENSE_HEADER_START@
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * 1.  Redistributions of source code must retain the above copyright
16 *     notice, this list of conditions and the following disclaimer.
17 * 2.  Redistributions in binary form must reproduce the above copyright
18 *     notice, this list of conditions and the following disclaimer in the
19 *     documentation and/or other materials provided with the distribution.
20 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
21 *     its contributors may be used to endorse or promote products derived
22 *     from this software without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
25 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
28 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 * @APPLE_BSD_LICENSE_HEADER_END@
36 */
37
38#ifdef HAVE_CONFIG_H
39#include <config.h>
40#endif
41
42#if HAVE_SYS_CDEFS_H
43#include <sys/cdefs.h>
44#endif
45
46#if defined(_DARWIN_FEATURE_64_BIT_INODE) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE)
47/* The architecture we're building for has multiple versions of stat.
48   We need to undo sys/cdefs.h changes for _DARWIN_FEATURE_64_BIT_INODE */
49#undef  __DARWIN_64_BIT_INO_T
50#define __DARWIN_64_BIT_INO_T 0
51#undef  __DARWIN_SUF_64_BIT_INO_T
52#define __DARWIN_SUF_64_BIT_INO_T ""
53#undef _DARWIN_FEATURE_64_BIT_INODE
54#endif
55
56#ifdef HAVE_CRT_EXTERNS_H
57#include <crt_externs.h>
58#endif
59
60#ifdef HAVE_SYS_PATHS_H
61#include <sys/paths.h>
62#endif
63
64#include <ctype.h>
65#include <dirent.h>
66#include <dlfcn.h>
67#include <errno.h>
68#include <fcntl.h>
69#include <pthread.h>
70#include <stdarg.h>
71#include <stdbool.h>
72#include <stdint.h>
73#include <stdio.h>
74#include <stdlib.h>
75#include <string.h>
76#include <sys/param.h>
77#include <sys/socket.h>
78#include <sys/stat.h>
79#include <sys/syscall.h>
80#include <sys/types.h>
81#include <sys/un.h>
82#include <unistd.h>
83
84#ifndef HAVE_STRLCPY
85/* Define strlcpy if it's not available. */
86size_t strlcpy(char *dst, const char *src, size_t size) {
87        size_t result = strlen(src);
88        if (size > 0) {
89                size_t copylen = size - 1;
90                if (copylen > result) {
91                        copylen = result;
92                }
93                memcpy(dst, src, copylen);
94                dst[copylen] = 0;
95        }
96        return result;
97}
98#endif
99
100#include "../pextlib1.0/strlcat.c"
101
102/* global variables (only checked when setup is first called)
103 * DARWINTRACE_LOG
104 *    path to the log file (no logging happens if it's unset).
105 * DARWINTRACE_SANDBOX_BOUNDS
106 *    : separated allowed paths for the creation of files.
107 *    \: -> :
108 *    \\ -> \
109 */
110
111/*
112 * DARWINTRACE_DEBUG: verbose output of operations to debug darwintrace
113 */
114#ifndef DARWINTRACE_DEBUG
115#define DARWINTRACE_DEBUG (0)
116#endif
117
118static inline int __darwintrace_strbeginswith(const char *str, const char *prefix);
119static inline int __darwintrace_pathbeginswith(const char *str, const char *prefix);
120static inline void __darwintrace_log_op(const char *op, const char *path, int fd);
121static void __darwintrace_copy_env() __attribute__((constructor));
122static void __darwintrace_setup_tls() __attribute__((constructor));
123static inline char *__darwintrace_alloc_env(const char *varName, const char *varValue);
124static inline char *const *__darwintrace_restore_env(char *const envp[]);
125static inline void __darwintrace_setup();
126static inline void __darwintrace_cleanup_path(char *path);
127static char *__send(const char *buf, uint32_t len, int answer);
128
129/**
130 * PID of the process darwintrace was last used in. This is used to detect
131 * forking and opening a new connection to the control socket in the child
132 * process. Not doing so would potentially cause two processes writing to the
133 * same socket.
134 */
135static pid_t __darwintrace_pid = (pid_t) - 1;
136
137/**
138 * pthread_key_ts for the pthread_t returned by pthread_self() and the
139 * darwintrace socket to ensure the socket is only used from a single thread.
140 */
141static pthread_key_t tid_key;
142static pthread_key_t sock_key;
143
144/**
145 * Helper variable containing the number of the darwintrace socket, iff the
146 * close(2) syscall should be allowed to close it. Used by \c
147 * __darwintrace_close.
148 */
149static volatile int __darwintrace_close_sock = -1;
150
151/**
152 * size of the communication buffer
153 */
154#define BUFFER_SIZE 1024
155
156/**
157 * Variable holding the sandbox bounds in the following format:
158 *  <filemap>       :: (<spec> '\0')+ '\0'
159 *  <spec>          :: <path> '\0' <operation> <additional_data>?
160 *  <operation>     :: '0' | '1' | '2'
161 * where
162 *  0: allow
163 *  1: map the path to the one given in additional_data
164 *  2: check for a dependency using the socket
165 */
166static char *filemap;
167
168enum {
169    FILEMAP_ALLOW = 0,
170    FILEMAP_REDIR = 1,
171    FILEMAP_ASK   = 2
172};
173
174/**
175 * Copy of the DYLD_INSERT_LIBRARIES environment variable to restore it in
176 * execve(2). DYLD_INSERT_LIBRARIES is needed to preload this library into any
177 * process' address space.
178 */
179static char *__env_dyld_insert_libraries;
180
181/**
182 * Copy of the DYLD_FORCE_FLAT_NAMESPACE environment variable to restore it in
183 * execve(2). DYLD_FORCE_FLAT_NAMESPACE=1 is needed for the preload-based
184 * sandbox to work.
185 */
186static char *__env_dyld_force_flat_namespace;
187
188/**
189 * Copy of the DARWINTRACE_LOG environment variable to restore it in execve(2).
190 * Contains the path to the unix socket used for communication with the
191 * MacPorts-side of the sandbox.
192 */
193static char *__env_darwintrace_log;
194
195#if DARWINTRACE_DEBUG
196#   if __STDC_VERSION__>=199901L
197#       define debug_printf(format, ...) \
198        fprintf(stderr, "darwintrace[%d]: " format, getpid(), __VA_ARGS__);
199#   else
200__attribute__((format(printf, 1, 2))) static inline void debug_printf(const char *format, ...) {
201        va_list args;
202        va_start(args, format);
203        vfprintf(stderr, format, args);
204        va_end(args);
205}
206#   endif
207#else
208#   define debug_printf(...)
209#endif
210
211/**
212 * Setup method called as constructor to set up thread-local storage for the
213 * thread id and the darwintrace socket.
214 */
215static void __darwintrace_setup_tls() {
216        if (0 != (errno = pthread_key_create(&tid_key, NULL))) {
217                perror("darwintrace: pthread_key_create");
218                abort();
219        }
220        if (0 != (errno = pthread_key_create(&sock_key, NULL))) {
221                perror("darwintrace: pthread_key_create");
222                abort();
223        }
224}
225
226/**
227 * Convenience getter function for the thread-local darwintrace socket
228 */
229static inline FILE *__darwintrace_sock() {
230        return (FILE *) pthread_getspecific(sock_key);
231}
232
233/**
234 * Convenience getter function for the thread ID
235 */
236static inline pthread_t __darwintrace_tid() {
237        return (pthread_t) pthread_getspecific(tid_key);
238}
239
240/**
241 * Convenience setter function for the thread-local darwintrace socket
242 */
243static inline void __darwintrace_sock_set(FILE *stream) {
244        if (0 != (errno = pthread_setspecific(sock_key, stream))) {
245                perror("darwintrace: pthread_setspecific");
246                abort();
247        }
248}
249
250/**
251 * Convenience setter function for the thread-local darwintrace socket
252 */
253static inline void __darwintrace_tid_set() {
254        if (0 != (errno = pthread_setspecific(tid_key, (const void *) pthread_self()))) {
255                perror("darwintrace: pthread_setspecific");
256                abort();
257        }
258}
259
260/**
261 * Return 0 if str doesn't begin with prefix, 1 otherwise. Note that this is
262 * not a simple string comparison, but works on a path component level.
263 * A prefix of /var/tmp will not match a string of /var/tmpfoo.
264 */
265static inline int __darwintrace_pathbeginswith(const char *str, const char *prefix) {
266        char s;
267        char p;
268
269        /* '/' is the allow all wildcard */
270        if (strcmp(prefix, "/") == 0) {
271                return 1;
272        }
273
274        do {
275                s = *str++;
276                p = *prefix++;
277        } while (p && (p == s));
278        return (p == 0 && (s == '/' || s == '\0'));
279}
280
281/**
282 * Return 0 if str doesn't begin with prefix, 1 otherwise.
283 */
284static inline int __darwintrace_strbeginswith(const char *str, const char *prefix) {
285        char s;
286        char p;
287        do {
288                s = *str++;
289                p = *prefix++;
290        } while (p && (p == s));
291        return (p == 0);
292}
293
294/*
295 * Copy the environment variables, if they're defined. This is run as
296 * a constructor at startup.
297 */
298static void __darwintrace_copy_env() {
299#define COPYENV(name, variable) \
300        if (NULL != (val = getenv(#name))) {\
301                if (NULL == (variable = strdup(val))) {\
302                        perror("darwintrace: strdup");\
303                        abort();\
304                }\
305        } else {\
306                variable = NULL;\
307        }
308
309        char *val;
310        COPYENV(DYLD_INSERT_LIBRARIES,     __env_dyld_insert_libraries)
311        COPYENV(DYLD_FORCE_FLAT_NAMESPACE, __env_dyld_force_flat_namespace)
312        COPYENV(DARWINTRACE_LOG,           __env_darwintrace_log)
313#undef COPYENV
314}
315
316/**
317 * Allocate a X=Y string where X is the variable name and Y its value.
318 * Return the new string.
319 *
320 * If the value is NULL, return NULL.
321 */
322static inline char *__darwintrace_alloc_env(const char *name, const char *val) {
323        char *result = NULL;
324
325        if (val) {
326                size_t size = strlen(name) + strlen(val) + 2;
327                if (NULL == (result = malloc(size))) {
328                        perror("darwintrace: malloc");
329                        abort();
330                }
331                snprintf(result, size, "%s=%s", name, val);
332                if (size > 0) {
333                        result[size - 1] = '\0';
334                }
335        }
336
337        return result;
338}
339
340/**
341 * This function checks that envp contains the global variables we had when the
342 * library was loaded and modifies it if it doesn't.
343 */
344static inline char *const *__darwintrace_restore_env(char *const envp[]) {
345        /* allocate the strings. */
346        /* we don't care about the leak here because we're going to call execve,
347         * which, if it succeeds, will get rid of our heap */
348        char *dyld_insert_libraries_ptr     = __darwintrace_alloc_env("DYLD_INSERT_LIBRARIES",     __env_dyld_insert_libraries);
349        char *dyld_force_flat_namespace_ptr = __darwintrace_alloc_env("DYLD_FORCE_FLAT_NAMESPACE", __env_dyld_force_flat_namespace);
350        char *darwintrace_log_ptr           = __darwintrace_alloc_env("DARWINTRACE_LOG",           __env_darwintrace_log);
351
352        char *const *enviter = envp;
353        size_t envlen = 0;
354        char **copy;
355        char **copyiter;
356
357        while (*enviter != NULL) {
358                envlen++;
359                enviter++;
360        }
361
362        /* 4 is sufficient for the three variables we copy and the terminator */
363        copy = malloc(sizeof(char *) * (envlen + 4));
364
365        enviter  = envp;
366        copyiter = copy;
367
368        while (*enviter != NULL) {
369                char *val = *enviter;
370                if (__darwintrace_strbeginswith(val, "DYLD_INSERT_LIBRARIES=")) {
371                        val = dyld_insert_libraries_ptr;
372                        dyld_insert_libraries_ptr = NULL;
373                } else if (__darwintrace_strbeginswith(val, "DYLD_FORCE_FLAT_NAMESPACE=")) {
374                        val = dyld_force_flat_namespace_ptr;
375                        dyld_force_flat_namespace_ptr = NULL;
376                } else if (__darwintrace_strbeginswith(val, "DARWINTRACE_LOG=")) {
377                        val = darwintrace_log_ptr;
378                        darwintrace_log_ptr = NULL;
379                }
380
381                if (val) {
382                        *copyiter++ = val;
383                }
384
385                enviter++;
386        }
387
388        if (dyld_insert_libraries_ptr) {
389                *copyiter++ = dyld_insert_libraries_ptr;
390        }
391        if (dyld_force_flat_namespace_ptr) {
392                *copyiter++ = dyld_force_flat_namespace_ptr;
393        }
394        if (darwintrace_log_ptr) {
395                *copyiter++ = darwintrace_log_ptr;
396        }
397
398        *copyiter = 0;
399
400        return copy;
401}
402
403/*
404 * Data structures and functions to iterate over the filemap received from
405 * tracelib code.
406 */
407
408/**
409 * \c filemap_iterator_t is an (opaque) iterator type that keeps the state
410 * required to iterate through the filemap. Create a new filemap_iterator_t on
411 * stack, initialize it using \c __darwintrace_filemap_iterator_init and pass
412 * it to \c __darwintrace_filemap_iter to iterate over the filemap.
413 */
414typedef struct filemap_iterator {
415        char *next;
416} filemap_iterator_t;
417
418/**
419 * Initialize a given \c filemap_iterator_t. Calling this function again will
420 * rewind the iterator.
421 *
422 * \param[in] it pointer to the iterator to be initialized
423 */
424static inline void __darwintrace_filemap_iterator_init(filemap_iterator_t *it) {
425        it->next = filemap;
426}
427
428/**
429 * Iterate through the filemap passed from tracelib code. Call this multiple
430 * times with the same iterator object until it returns \c NULL to iterate
431 * through the filemap.
432 *
433 * \param[out] command location for the command specified for this filemap
434 *                     entry
435 * \param[out] replacement location for a replacement path, if any. This field
436 *                         is only valid if the command field indicates
437 *                         a replacement path is being used.
438 * \param[in]  it pointer to a \c filemap_iterator_t keeping the state of this
439 *                iteration
440 * \return string containing the path this filemap entry corresponds to, or \c
441 *         NULL if the end of the filemap was reached
442 */
443static inline char *__darwintrace_filemap_iter(char *command, char **replacement, filemap_iterator_t *it) {
444        enum { PATH, COMMAND, REPLACEPATH, DONE } state = PATH;
445        char *t;
446        char *path;
447
448        if (it == NULL || it->next == NULL || *it->next == '\0') {
449                return NULL;
450        }
451
452        path = t = it->next;
453
454        /* advance the cursor: if the number after the string is not 1, there's no
455         * path behind it and we can advance by strlen(t) + 3. If it is 1, make
456         * sure to skip the path, too.
457         */
458        state = PATH;
459        while (state != DONE) {
460                switch (state) {
461                        case DONE:
462                                fprintf(stderr, "darwintrace: illegal state in dfa in " __FILE__ ":%d\n", __LINE__);
463                                abort();
464                                break;
465                        case PATH:
466                                if (!*t) {
467                                        state = COMMAND;
468                                }
469                                break;
470                        case COMMAND:
471                                *command = *t;
472                                if (*t == 1) {
473                                        state = REPLACEPATH;
474                                        *replacement = t + 1;
475                                } else {
476                                        state = DONE;
477                                        /* the byte after the status code is 0, if the status
478                                         * code isn't 1 */
479                                        t++;
480                                }
481                                break;
482                        case REPLACEPATH:
483                                if (!*t) {
484                                        state = DONE;
485                                }
486                                break;
487                }
488                t++;
489        }
490
491        it->next = t;
492        return path;
493}
494
495/**
496 * Request sandbox boundaries from tracelib (the MacPorts base-controlled side
497 * of the trace setup) and store it.
498 */
499static void __darwintrace_get_filemap() {
500        char *newfilemap;
501#if DARWINTRACE_DEBUG && 0
502        filemap_iterator_t it;
503        char *path, *replacement, command;
504#endif
505
506#if __GNUC__ && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))
507#error Please build with gcc-4.2 or later
508#endif
509
510        /*
511         * ensure we have a filemap present; this might be called simultanously
512         * from multiple threads and needs to work without leaking and in a way
513         * that ensures a filemap has been set before any of the calls return. We
514         * achieve that by using non-blocking synchronization. Blocking
515         * synchronization might be a bad idea, because we never know where this
516         * code is actually called in an application.
517         */
518        newfilemap = NULL;
519        do {
520                free(newfilemap);
521                if (filemap != NULL)
522                        break;
523                newfilemap = __send("filemap\t", (uint32_t) strlen("filemap\t"), 1);
524        } while (!__sync_bool_compare_and_swap(&filemap, NULL, newfilemap));
525
526#if DARWINTRACE_DEBUG && 0
527        for (__darwintrace_filemap_iterator_init(&it);
528                (path = __darwintrace_filemap_iter(&command, &replacement, &it));) {
529                debug_printf("filemap: {cmd=%d, path=%-120s, replacement=%s}\n", command, path, (command == 1) ? replacement : "-");
530        }
531#endif
532}
533
534/**
535 * Close the darwintrace socket and set it to \c NULL. Since this uses \c
536 * fclose(3), which internally calls \c close(2), which is intercepted by this
537 * library and this library prevents closing the socket to MacPorts, we use \c
538 * __darwintrace_close_sock to allow closing specific FDs.
539 */
540static inline void __darwintrace_close() {
541        FILE *dtsock = __darwintrace_sock();
542        if (dtsock) {
543                __darwintrace_close_sock = fileno(dtsock);
544                fclose(dtsock);
545                __darwintrace_close_sock = -1;
546                pthread_setspecific(sock_key, NULL);
547        }
548}
549
550/**
551 * Ensures darwintrace is correctly set up by opening a socket connection to
552 * the MacPorts-side of trace mode. Will close an re-open this connection when
553 * called after \c fork(2), i.e. when the current PID doesn't match the one
554 * stored when the function was called last.
555 */
556static inline void __darwintrace_setup() {
557        /*
558         * Check whether this is a child process and we've inherited the socket. We
559         * want to avoid race conditions with our parent process when communicating
560         * with tracelib and thus re-open all sockets, if that's the case. Note
561         * this also applies to threads within the same process, since we really
562         * want to avoid mixing up the results from two calls in different threads
563         * when reading from the socket.
564         */
565
566        /*
567         * if the PID changed, close the current socket (which will force the
568         * following code to re-open it).
569         */
570        if (__darwintrace_pid != (pid_t) -1 && __darwintrace_pid != getpid()) {
571                __darwintrace_close();
572                __darwintrace_pid = (pid_t) -1;
573        }
574
575        /*
576         * We don't need to watch for TID changes, because each thread has thread
577         * local storage for the socket that will contain NULL when the socket has
578         * not been initialized.
579         */
580
581        if (__darwintrace_sock() == NULL) {
582                int sock;
583                FILE *stream;
584                struct sockaddr_un sun;
585
586                __darwintrace_pid = getpid();
587                __darwintrace_tid_set();
588                if (__env_darwintrace_log == NULL) {
589                        fprintf(stderr, "darwintrace: trace library loaded, but DARWINTRACE_LOG not set\n");
590                        abort();
591                }
592
593                if (-1 == (sock = socket(PF_LOCAL, SOCK_STREAM, 0))) {
594                        perror("darwintrace: socket");
595                        abort();
596                }
597
598                if (strlen(__env_darwintrace_log) > sizeof(sun.sun_path) - 1) {
599                        fprintf(stderr, "darwintrace: Can't connect to socket %s: name too long\n", __env_darwintrace_log);
600                        abort();
601                }
602                sun.sun_family = AF_UNIX;
603                strlcpy(sun.sun_path, __env_darwintrace_log, sizeof(sun.sun_path));
604
605                if (-1 == (connect(sock, (struct sockaddr *) &sun, sizeof(sun)))) {
606                        perror("darwintrace: connect");
607                        abort();
608                }
609
610                if (NULL == (stream = fdopen(sock, "a+"))) {
611                        perror("darwintrace: fdopen");
612                        abort();
613                }
614
615                /* store FILE * into thread local storage for the socket */
616                __darwintrace_sock_set(stream);
617
618                /* request sandbox bounds */
619                __darwintrace_get_filemap();
620        }
621}
622
623/**
624 * Send a path to tracelib either given a path, or an FD (where
625 * fcntl(F_GETPATH) will be used).
626 *
627 * \param[in] op the operation (sent as-is to tracelib, should be interpreted
628 *               as command)
629 * \param[in] path the (not necessarily absolute) path to send to tracelib
630 * \param[in] fd a FD to the file, or 0, if none available
631 */
632static inline void __darwintrace_log_op(const char *op, const char *path, int fd) {
633        uint32_t size;
634        char somepath[MAXPATHLEN];
635        char logbuffer[BUFFER_SIZE];
636
637        do {
638#       ifdef __APPLE__ /* Only Darwin has volfs and F_GETPATH */
639                if ((fd > 0) && (strncmp(path, "/.vol/", 6) == 0)) {
640                        if (fcntl(fd, F_GETPATH, somepath) != -1) {
641                                break;
642                        }
643                }
644#       endif
645
646                if (*path != '/') {
647                        if (!getcwd(somepath, sizeof(somepath))) {
648                                perror("darwintrace: getcwd");
649                                abort();
650                        }
651
652                        strlcat(somepath, "/", sizeof(somepath));
653                        strlcat(somepath, path, sizeof(somepath));
654                        break;
655                }
656
657                /* otherwise, just copy the original path. */
658                strlcpy(somepath, path, sizeof(somepath));
659        } while (0);
660
661        /* clean the path. */
662        __darwintrace_cleanup_path(somepath);
663
664        size = snprintf(logbuffer, sizeof(logbuffer), "%s\t%s", op, somepath);
665        __send(logbuffer, size, 0);
666}
667
668/**
669 * remap resource fork access to the data fork.
670 * do a partial realpath(3) to fix "foo//bar" to "foo/bar"
671 */
672static inline void __darwintrace_cleanup_path(char *path) {
673        size_t pathlen;
674#   ifdef __APPLE__
675        size_t rsrclen;
676#   endif
677        char *dst, *src;
678        enum { SAWSLASH, NOTHING } state = NOTHING;
679
680        /* if this is a foo/..namedfork/rsrc, strip it off */
681        pathlen = strlen(path);
682        /* ..namedfork/rsrc is only on OS X */
683#   ifdef __APPLE__
684        rsrclen = strlen(_PATH_RSRCFORKSPEC);
685        if (pathlen > rsrclen && 0 == strcmp(path + pathlen - rsrclen, _PATH_RSRCFORKSPEC)) {
686                path[pathlen - rsrclen] = '\0';
687                pathlen -= rsrclen;
688        }
689#   endif
690
691        /* for each position in string, check if we're in a run of multiple
692         * slashes, and only emit the first one */
693        for (src = path, dst = path; *src; src++) {
694                if (state == SAWSLASH) {
695                        if (*src == '/') {
696                                /* consume it */
697                                continue;
698                        }
699                        state = NOTHING;
700                } else {
701                        if (*src == '/') {
702                                state = SAWSLASH;
703                        }
704                }
705                if (dst != src) {
706                        // if dst == src, avoid the copy operation
707                        *dst = *src;
708                }
709                dst++;
710        }
711}
712
713/**
714 * Check whether the port currently being installed declares a dependency on
715 * a given file. Communicates with MacPorts tracelib, which uses the registry
716 * database to answer this question. Returns 1, if a dependency was declared,
717 * 0, if the file belongs to a port and no dependency was declared and -1 if
718 * the file isnt't registered to any port.
719 *
720 * \param[in] path the path to send to MacPorts for dependency info
721 * \return 1, if access should be granted, 0, if access should be denied, and
722 *         -1 if MacPorts doesn't know about the file.
723 */
724static int dependency_check(char *path) {
725#define stat(y, z) syscall(SYS_stat, (y), (z))
726        char buffer[BUFFER_SIZE], *p;
727        uint32_t len;
728        int result = 0;
729        struct stat st;
730
731        if (-1 == stat(path, &st)) {
732                return 1;
733        }
734        if (S_ISDIR(st.st_mode)) {
735                debug_printf("%s is directory\n", path);
736                return 1;
737        }
738
739        len = snprintf(buffer, sizeof(buffer), "dep_check\t%s", path);
740        if (len > sizeof(buffer)) {
741                len = sizeof(buffer) - 1;
742        }
743        p = __send(buffer, len, 1);
744        if (!p) {
745                fprintf(stderr, "darwintrace: dependency check failed for %s\n", path);
746                abort();
747        }
748
749        switch (*p) {
750                case '+':
751                        result = 1;
752                        break;
753                case '!':
754                        result = 0;
755                        break;
756                case '?':
757                        result = -1;
758                        break;
759                default:
760                        fprintf(stderr, "darwintrace: unexpected answer from tracelib: '%c' (0x%x)\n", *p, *p);
761                        abort();
762                        break;
763        }
764
765        debug_printf("dependency_check: %s returned %d\n", path, result);
766
767        free(p);
768        return result;
769#undef stat
770}
771
772/**
773 * Helper function to recieve a number of bytes from the tracelib communication
774 * socket and deal with any errors that might occur.
775 *
776 * \param[out] buf buffer to hold received data
777 * \param[in]  size number of bytes to read from the socket
778 */
779static void frecv(void *restrict buf, size_t size) {
780        FILE *stream = __darwintrace_sock();
781        if (1 != fread(buf, size, 1, stream)) {
782                if (ferror(stream)) {
783                        perror("darwintrace: fread");
784                } else {
785                        fprintf(stderr, "darwintrace: fread: end-of-file\n");
786                }
787                abort();
788        }
789}
790
791/**
792 * Helper function to send a buffer to MacPorts using the tracelib
793 * communication socket and deal with any errors that might occur.
794 *
795 * \param[in] buf buffer to send
796 * \param[in] size number of bytes in the buffer
797 */
798static void fsend(const void *restrict buf, size_t size) {
799        FILE *stream = __darwintrace_sock();
800        if (1 != fwrite(buf, size, 1, stream)) {
801                if (ferror(stream)) {
802                        perror("darwintrace: fwrite");
803                } else {
804                        fprintf(stderr, "darwintrace: fwrite: end-of-file\n");
805                }
806                abort();
807        }
808        fflush(stream);
809}
810
811/**
812 * Communication wrapper targeting tracelib. Automatically enforces the on-wire
813 * protocol and supports reading and returning an answer.
814 *
815 * \param[in] buf buffer to send to tracelib
816 * \param[in] size size of the buffer to send
817 * \param[in] answer boolean indicating whether an answer is expected and
818 *                   should be returned
819 * \return allocated answer buffer. Callers should free this buffer. If an
820 *         answer was not requested, \c NULL.
821 */
822static char *__send(const char *buf, uint32_t len, int answer) {
823        fsend(&len, sizeof(len));
824        fsend(buf, len);
825
826        if (!answer) {
827                return NULL;
828        }
829
830        uint32_t recv_len = 0;
831        char *recv_buf;
832
833        frecv(&recv_len, sizeof(recv_len));
834        if (recv_len == 0) {
835                return 0;
836        }
837
838        recv_buf = malloc(recv_len + 1);
839        recv_buf[recv_len] = '\0';
840        frecv(recv_buf, recv_len);
841
842        return recv_buf;
843}
844
845/**
846 * Check a path against the current sandbox
847 *
848 * \param[in] path the path to be checked; not necessarily absolute
849 * \param[out] newpath buffer for a replacement path when redirection should
850 *                     occur. Initialize the first byte with 0 before calling
851 *                     this function. The buffer should be at least MAXPATHLEN
852 *                     bytes large. If newpath[0] isn't 0 after the call,
853 *                     redirection should occur and the path from newpath
854 *                     should be used for the syscall instead.
855 * \param[in] report If access to this path is being denied, report it as
856 *                   sandbox violation. Set this to \c true for all operations
857 *                   that read file contents. Set this to \c false for
858 *                   operations that only check for the file's existance, e.g.,
859 *                   reading a directory.
860 * \return 1, if the file is within sandbox bounds, 0, if access should be denied
861 */
862static inline int __darwintrace_is_in_sandbox(const char *path, char *newpath, bool report) {
863        char *t, *_;
864        char *strpos, *normpos;
865        char lpath[MAXPATHLEN];
866        char normalizedpath[MAXPATHLEN];
867        filemap_iterator_t filemap_it;
868        char command;
869        char *replacementpath;
870
871        __darwintrace_setup();
872
873        if (!filemap) {
874                return 1;
875        }
876
877        /* Make sure the path is absolute. */
878        if (*path == '/') {
879                strcpy(lpath, path);
880        } else {
881                if (getcwd(lpath, MAXPATHLEN - 1) == NULL) {
882                        perror("darwintrace: getcwd");
883                        abort();
884                }
885                strlcat(lpath, "/", MAXPATHLEN);
886                strlcat(lpath, path, MAXPATHLEN);
887        }
888
889        /* Make sure the path is normalized. NOTE: Do _not_ use realpath(3) here.
890         * Doing so _will_ lead to problems. This is essentially a very simple
891         * re-implementation of realpath(3). */
892        normalizedpath[0] = '\0';
893        strpos = lpath + 1;
894        normpos = normalizedpath;
895        for (;;) {
896                char *curpos = strsep(&strpos, "/");
897                if (curpos == NULL) {
898                        /* reached the end of the path */
899                        break;
900                } else if (*curpos == '\0') {
901                        /* empty entry, ignore */
902                        continue;
903                } else if (strcmp(curpos, ".") == 0) {
904                        /* no-op directory, ignore */
905                        continue;
906                } else if (strcmp(curpos, "..") == 0) {
907                        /* walk up one directory */
908                        char *lastSep = strrchr(normalizedpath, '/');
909                        if (lastSep == NULL) {
910                                /* path is completely empty */
911                                normpos = normalizedpath;
912                                *normpos = '\0';
913                                continue;
914                        }
915                        /* remove last component by overwriting the slash with \0, update normpos */
916                        *lastSep = '\0';
917                        normpos = lastSep;
918                        continue;
919                }
920                /* default case: standard path, copy */
921                strcat(normpos, "/");
922                normpos++;
923                strcat(normpos, curpos);
924        }
925        if (*normalizedpath == '\0') {
926                strcat(normalizedpath, "/");
927        }
928
929        /* Iterate over the sandbox bounds and try to find a directive matching this path */
930        for (__darwintrace_filemap_iterator_init(&filemap_it);
931                (t = __darwintrace_filemap_iter(&command, &replacementpath, &filemap_it));) {
932                if (__darwintrace_pathbeginswith(normalizedpath, t)) {
933                        /* move t to the integer describing how to handle this match */
934                        t += strlen(t) + 1;
935                        switch (*t) {
936                                case FILEMAP_ALLOW:
937                                        return 1;
938                                case FILEMAP_REDIR:
939                                        if (!newpath) {
940                                                return 0;
941                                        }
942                                        /* the redirected path starts right after the byte telling
943                                         * us we should redirect */
944                                        strcpy(newpath, t + 1);
945                                        _ = newpath + strlen(newpath);
946                                        /* append '/' if it's missing */
947                                        if (_[-1] != '/') {
948                                                *_++ = '/';
949                                        }
950                                        strcpy(_, normalizedpath);
951                                        return 1;
952                                case FILEMAP_ASK:
953                                        /* ask the socket whether this file is OK */
954                                        switch (dependency_check(normalizedpath)) {
955                                                case 1:
956                                                        return 1;
957                                                case -1:
958                                                        /* if the file isn't known to MacPorts, allow
959                                                         * access anyway, but report a sandbox violation.
960                                                         * TODO find a better solution */
961                                                        if (report)
962                                                                __darwintrace_log_op("sandbox_violation", normalizedpath, 0);
963                                                        return 1;
964                                                case 0:
965                                                        /* file belongs to a foreign port, deny access */
966                                                        if (report)
967                                                                __darwintrace_log_op("sandbox_violation", normalizedpath, 0);
968                                                        return 0;
969                                        }
970                                default:
971                                        fprintf(stderr, "darwintrace: error: unexpected byte in file map: `%x'\n", *t);
972                                        abort();
973                        }
974                }
975        }
976
977        if (report)
978                __darwintrace_log_op("sandbox_violation", normalizedpath, 0);
979        return 0;
980}
981
982/* wrapper for open(2) preventing opening files outside the sandbox */
983int open(const char *path, int flags, ...) {
984#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
985        mode_t mode;
986        va_list args;
987        char newpath[MAXPATHLEN];
988
989        debug_printf("open(%s)\n", path);
990
991        *newpath = '\0';
992        if (!__darwintrace_is_in_sandbox(path, newpath, true)) {
993                debug_printf("open %s was forbidden\n", path);
994                errno = ((flags & O_CREAT) > 0) ? EACCES : ENOENT;
995                return -1;
996        }
997
998        if (*newpath) {
999                path = newpath;
1000        }
1001
1002        /* Why mode here ? */
1003        va_start(args, flags);
1004        mode = va_arg(args, int);
1005        va_end(args);
1006
1007        return open(path, flags, mode);
1008#undef open
1009}
1010
1011/* Log calls to readlink(2) into the file specified by DARWINTRACE_LOG.
1012   Only logs if the DARWINTRACE_LOG environment variable is set.
1013   Only logs files where the readlink succeeds.
1014*/
1015#ifdef READLINK_IS_NOT_P1003_1A
1016int readlink(const char *path, char *buf, int bufsiz) {
1017#else
1018ssize_t readlink(const char *path, char *buf, size_t bufsiz) {
1019#endif
1020#define readlink(x,y,z) syscall(SYS_readlink, (x), (y), (z))
1021        char newpath[MAXPATHLEN];
1022
1023        debug_printf("readlink(%s)\n", path);
1024
1025        *newpath = '\0';
1026        if (!__darwintrace_is_in_sandbox(path, newpath, true)) {
1027                errno = ENOENT;
1028                return -1;
1029        }
1030
1031        if (*newpath) {
1032                path = newpath;
1033        }
1034
1035        return readlink(path, buf, bufsiz);
1036#undef readlink
1037}
1038
1039int execve(const char *path, char *const argv[], char *const envp[]) {
1040#define execve(x,y,z) syscall(SYS_execve, (x), (y), (z))
1041#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
1042#define close(x) syscall(SYS_close, (x))
1043#define lstat(x, y) syscall(SYS_lstat, (x), (y))
1044        debug_printf("execve(%s)\n", path);
1045        __darwintrace_setup();
1046        struct stat sb;
1047        /* for symlinks, we want to capture both the original path and the modified
1048         * one, since for $prefix/bin/gcc -> mp-gcc-4.8, both "gcc_select" and
1049         * "gcc48" are contributors. This requires changes to the select code such
1050         * that the symlinks are registered to the *_select ports. Since this
1051         * a general problem (when executing $prefix/libexec/mysql/bin/foo where
1052         * $prefix/libexec/mysql is a symlink to $prefix/libexec/mysql55, the
1053         * mysql_select port needs to be a contributor!) we should really implement
1054         * this in __darwintrace_is_in_sandbox().
1055         */
1056        if (lstat(path, &sb) == 0) {
1057                if (!__darwintrace_is_in_sandbox(path, NULL, true)) {
1058                        errno = ENOENT;
1059                        return -1;
1060                }
1061
1062                int fd = open(path, O_RDONLY, 0);
1063                if (fd > 0) {
1064                        char buffer[MAXPATHLEN + 1];
1065                        ssize_t bytes_read;
1066
1067                        /* Read the file for the interpreter. Fortunately, on OS X:
1068                         *   The system guarantees to read the number of bytes requested if
1069                         *   the descriptor references a normal file that has that many
1070                         *   bytes left before the end-of-file, but in no other case.
1071                         * That _does_ save us another ugly loop to get things right. */
1072                        bytes_read = read(fd, buffer, MAXPATHLEN);
1073                        buffer[bytes_read] = '\0';
1074                        const char *buffer_end = buffer + bytes_read;
1075                        if (bytes_read > 2 && buffer[0] == '#' && buffer[1] == '!') {
1076                                char *interp = buffer + 2;
1077
1078                                /* skip past leading whitespace */
1079                                while (interp < buffer_end && isblank(*interp)) {
1080                                        ++interp;
1081                                }
1082                                /* found interpreter (or ran out of data); skip until next
1083                                 * whitespace, then terminate the string */
1084                                if (interp < buffer_end) {
1085                                        char *interp_end = interp;
1086                                        strsep(&interp_end, " \t");
1087                                }
1088
1089                                /* check the iterpreter against the sandbox */
1090                                if (!__darwintrace_is_in_sandbox(interp, NULL, true)) {
1091                                        close(fd);
1092                                        errno = ENOENT;
1093                                        return -1;
1094                                }
1095                        }
1096
1097                        close(fd);
1098
1099                }
1100        }
1101
1102        /* our variables won't survive exec, clean up */
1103        __darwintrace_close();
1104        __darwintrace_pid = (pid_t) - 1;
1105
1106        /* call the original execve function, but fix the environment if required. */
1107        return execve(path, argv, __darwintrace_restore_env(envp));
1108#undef lstat
1109#undef close
1110#undef open
1111#undef execve
1112}
1113
1114/* if darwintrace has been initialized, trap attempts to close our file
1115 * descriptor */
1116int close(int fd) {
1117#define close(x) syscall(SYS_close, (x))
1118        FILE *stream = __darwintrace_sock();
1119        if (stream) {
1120                int dtsock = fileno(stream);
1121                if (fd == dtsock && dtsock != __darwintrace_close_sock) {
1122                        errno = EBADF;
1123                        return -1;
1124                }
1125        }
1126
1127        return close(fd);
1128#undef close
1129}
1130
1131/* if darwintrace has been initialized, trap attempts to dup2 over our file descriptor */
1132int dup2(int filedes, int filedes2) {
1133#define dup2(x, y) syscall(SYS_dup2, (x), (y))
1134        FILE *stream = __darwintrace_sock();
1135
1136        debug_printf("dup2(%d, %d)\n", filedes, filedes2);
1137        if (stream && filedes2 == fileno(stream)) {
1138                /* if somebody tries to close our file descriptor, just move it out of
1139                 * the way. Make sure it doesn't end up as stdin/stdout/stderr, though!
1140                 * */
1141                int new_darwintrace_fd;
1142                FILE *new_stream;
1143
1144                if (-1 == (new_darwintrace_fd = fcntl(fileno(stream), F_DUPFD, STDOUT_FILENO + 1))) {
1145                        /* if duplicating fails, do not allow overwriting either! */
1146                        return -1;
1147                }
1148
1149                debug_printf("moving __darwintrace FD from %d to %d\n", fileno(stream), new_darwintrace_fd);
1150                __darwintrace_close();
1151                if (NULL == (new_stream = fdopen(new_darwintrace_fd, "a+"))) {
1152                        perror("darwintrace: fdopen");
1153                        abort();
1154                }
1155                __darwintrace_sock_set(new_stream);
1156        }
1157
1158        return dup2(filedes, filedes2);
1159#undef dup2
1160}
1161
1162/* Trap attempts to unlink a file outside the sandbox. */
1163int unlink(const char *path) {
1164#define __unlink(x) syscall(SYS_unlink, (x))
1165        char newpath[MAXPATHLEN];
1166
1167        *newpath = '\0';
1168        if (!__darwintrace_is_in_sandbox(path, newpath, true)) {
1169                debug_printf("unlink %s was forbidden\n", path);
1170                errno = ENOENT;
1171                return -1;
1172        }
1173
1174        if (*newpath) {
1175                path = newpath;
1176        }
1177
1178        debug_printf("unlink %s was allowed\n", path);
1179
1180        return __unlink(path);
1181}
1182
1183/* Trap attempts to create directories outside the sandbox.
1184 */
1185int mkdir(const char *path, mode_t mode) {
1186#define __mkdir(x,y) syscall(SYS_mkdir, (x), (y))
1187        char newpath[MAXPATHLEN];
1188
1189        *newpath = '\0';
1190        if (!__darwintrace_is_in_sandbox(path, newpath, true)) {
1191                struct stat st;
1192                if (-1 == lstat(path, &st)) {
1193                        if (errno == ENOENT) {
1194                                /* directory doesn't exist yet */
1195                                debug_printf("mkdir was forbidden at %s\n", path);
1196                                errno = EACCES;
1197                                return -1;
1198                        }
1199                }
1200                /* otherwise, mkdir will do nothing or fail with a hopefully meaningful
1201                 * error */
1202        } else {
1203                if (*newpath) {
1204                        path = newpath;
1205                }
1206
1207                debug_printf("mkdir was allowed at %s\n", path);
1208        }
1209
1210        return __mkdir(path, mode);
1211}
1212
1213/* Trap attempts to remove directories outside the sandbox.
1214 */
1215int rmdir(const char *path) {
1216#define __rmdir(x) syscall(SYS_rmdir, (x))
1217        if (!__darwintrace_is_in_sandbox(path, NULL, true)) {
1218                debug_printf("removing directory %s was forbidden\n", path);
1219                errno = ENOENT;
1220                return -1;
1221        }
1222
1223        debug_printf("rmdir %s was allowed\n", path);
1224
1225        return __rmdir(path);
1226}
1227
1228/* Trap attempts to rename files/directories outside the sandbox.
1229 */
1230int rename(const char *from, const char *to) {
1231#define __rename(x,y) syscall(SYS_rename, (x), (y))
1232        if (!__darwintrace_is_in_sandbox(from, NULL, true)) {
1233                /* outside sandbox, forbid */
1234                debug_printf("renaming from %s was forbidden\n", from);
1235                errno = ENOENT;
1236                return -1;
1237        }
1238        if (!__darwintrace_is_in_sandbox(to, NULL, true)) {
1239                debug_printf("renaming to %s was forbidden\n", to);
1240                errno = EACCES;
1241                return -1;
1242        }
1243
1244        debug_printf("renaming from %s to %s was allowed\n", from, to);
1245
1246        return __rename(from, to);
1247}
1248
1249int stat(const char *path, struct stat *sb) {
1250#define stat(path, sb) syscall(SYS_stat, path, sb)
1251        int result = 0;
1252        char newpath[MAXPATHLEN];
1253
1254        debug_printf("stat(%s)\n", path);
1255        if (-1 == (result = stat(path, sb))) {
1256                return -1;
1257        }
1258
1259        if (S_ISDIR(sb->st_mode)) {
1260                return result;
1261        }
1262
1263        *newpath = '\0';
1264        if (!__darwintrace_is_in_sandbox(path, newpath, true)) {
1265                errno = ENOENT;
1266                return -1;
1267        }
1268
1269        if (*newpath) {
1270                result = stat(newpath, sb);
1271        }
1272
1273        return result;
1274#undef stat
1275}
1276
1277#if defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE)
1278
1279int stat64(const char *path, struct stat64 *sb) {
1280#define stat64(path, sb) syscall(SYS_stat64, path, sb)
1281        int result = 0;
1282        char newpath[MAXPATHLEN];
1283
1284        debug_printf("stat64(%s)\n", path);
1285        if (-1 == (result = stat64(path, sb))) {
1286                return -1;
1287        }
1288
1289        if (S_ISDIR(sb->st_mode)) {
1290                return result;
1291        }
1292
1293        *newpath = '\0';
1294        if (!__darwintrace_is_in_sandbox(path, newpath, true)) {
1295                errno = ENOENT;
1296                return -1;
1297        }
1298
1299        if (*newpath) {
1300                result = stat64(newpath, sb);
1301        }
1302
1303        return result;
1304#undef stat64
1305}
1306
1307int stat$INODE64(const char *path, struct stat64 *sb) {
1308        return stat64(path, sb);
1309}
1310
1311#endif /* defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE) */
1312
1313
1314int lstat(const char *path, struct stat *sb) {
1315#define lstat(path, sb) syscall(SYS_lstat, path, sb)
1316        int result = 0;
1317        char newpath[MAXPATHLEN];
1318
1319        debug_printf("lstat(%s)\n", path);
1320        if (-1 == (result = lstat(path, sb))) {
1321                return -1;
1322        }
1323
1324        if (S_ISDIR(sb->st_mode)) {
1325                return result;
1326        }
1327
1328        *newpath = '\0';
1329        if (!__darwintrace_is_in_sandbox(path, newpath, true)) {
1330                errno = ENOENT;
1331                return -1;
1332        }
1333
1334        if (*newpath) {
1335                result = lstat(newpath, sb);
1336        }
1337
1338        return result;
1339#undef lstat
1340}
1341
1342#if defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE)
1343
1344int lstat64(const char *path, struct stat64 *sb) {
1345#define lstat64(path, sb) syscall(SYS_lstat64, path, sb)
1346        int result = 0;
1347        char newpath[MAXPATHLEN];
1348
1349        debug_printf("lstat64(%s)\n", path);
1350        if (-1 == (result = lstat64(path, sb))) {
1351                return -1;
1352        }
1353
1354        if (S_ISDIR(sb->st_mode)) {
1355                return result;
1356        }
1357
1358        *newpath = '\0';
1359        if (!__darwintrace_is_in_sandbox(path, newpath, true)) {
1360                errno = ENOENT;
1361                return -1;
1362        }
1363
1364        if (*newpath) {
1365                result = lstat64(newpath, sb);
1366        }
1367
1368        return result;
1369#undef lstat64
1370}
1371
1372int lstat$INODE64(const char *path, struct stat64 *sb) {
1373        return lstat64(path, sb);
1374}
1375
1376#endif /* defined(__DARWIN_64_BIT_INO_T) && !defined(_DARWIN_FEATURE_ONLY_64_BIT_INODE) */
1377
1378/**
1379 * re-implementation of getdirent(2) and __getdirent64(2) preventing paths
1380 * outside the sandbox to show up when reading the contents of a directory.
1381 * Unfortunately, since we need to access the contents of the buffer, but the
1382 * contents differ by architecture, we can not rely on the dirent structure
1383 * defined by the header included by this program, because we don't know
1384 * whether darwintrace.dylib has been compiled for 64bit or 32bit inodes. We
1385 * thus copy both structs and decide at runtime.
1386 */
1387
1388#ifdef __APPLE__
1389/* only do this on mac, because fcntl(fd, F_GETPATH) might not be available on
1390 * other systems, and because other system's syscall names are probably
1391 * different anyway */
1392
1393#if defined(__DARWIN_64_BIT_INO_T)
1394
1395struct dirent64  {
1396        __uint64_t  d_ino;      /* file number of entry */
1397        __uint64_t  d_seekoff;  /* seek offset */
1398        __uint16_t  d_reclen;   /* length of this record */
1399        __uint16_t  d_namlen;   /* length of string in d_name */
1400        __uint8_t   d_type;     /* file type */
1401        char      d_name[__DARWIN_MAXPATHLEN]; /* entry name (up to MAXPATHLEN bytes) */
1402};
1403
1404size_t __getdirentries64(int fd, void *buf, size_t bufsize, __darwin_off_t *basep) {
1405#define __getdirentries64(w,x,y,z) syscall(SYS_getdirentries64, (w), (x), (y), (z))
1406        size_t sz = __getdirentries64(fd, buf, bufsize, basep);
1407        char dirname[MAXPATHLEN];
1408        size_t dnamelen;
1409
1410        if (-1 == fcntl(fd, F_GETPATH, dirname)) {
1411                errno = EBADF;
1412                return -1;
1413        }
1414
1415        dnamelen = strlen(dirname);
1416        if (dirname[dnamelen - 1] != '/') {
1417                dirname[dnamelen] = '/';
1418                dirname[dnamelen + 1] = '\0';
1419                dnamelen++;
1420        }
1421
1422        dnamelen = strlen(dirname);
1423        size_t offset;
1424        for (offset = 0; offset < sz;) {
1425                struct dirent64 *dent = (struct dirent64 *)(((char *) buf) + offset);
1426                dirname[dnamelen] = '\0';
1427                strcat(dirname, dent->d_name);
1428                if (!__darwintrace_is_in_sandbox(dirname, NULL, false)) {
1429                        debug_printf("__getdirentries64: filtered %s\n", dirname);
1430                        dent->d_ino = 0;
1431                } else {
1432                        debug_printf("__getdirentries64:  allowed %s\n", dirname);
1433                }
1434                offset += dent->d_reclen;
1435        }
1436
1437        return sz;
1438#undef __getdirentries64
1439}
1440
1441#endif /* defined(__DARWIN_64_BIT_INO_T) */
1442
1443#pragma pack(4)
1444struct dirent32 {
1445        ino_t d_ino;            /* file number of entry */
1446        __uint16_t d_reclen;    /* length of this record */
1447        __uint8_t  d_type;      /* file type */
1448        __uint8_t  d_namlen;    /* length of string in d_name */
1449        char d_name[__DARWIN_MAXNAMLEN + 1]; /* name must be no longer than this */
1450};
1451#pragma pack()
1452
1453int getdirentries(int fd, char *buf, int nbytes, long *basep) {
1454#define getdirentries(w,x,y,z) syscall(SYS_getdirentries, (w), (x), (y), (z))
1455        size_t sz = getdirentries(fd, buf, nbytes, basep);
1456        char dirname[MAXPATHLEN];
1457        size_t dnamelen;
1458
1459        if (-1 == fcntl(fd, F_GETPATH, dirname)) {
1460                errno = EBADF;
1461                return 0;
1462        }
1463
1464        dnamelen = strlen(dirname);
1465        if (dirname[dnamelen - 1] != '/') {
1466                dirname[dnamelen] = '/';
1467                dirname[dnamelen + 1] = '\0';
1468                dnamelen++;
1469        }
1470
1471        size_t offset;
1472        for (offset = 0; offset < sz;) {
1473                struct dirent32 *dent = (struct dirent32 *)(buf + offset);
1474                dirname[dnamelen] = '\0';
1475                strcat(dirname, dent->d_name);
1476                if (!__darwintrace_is_in_sandbox(dirname, NULL, false)) {
1477                        debug_printf("getdirentries: filtered %s\n", dirname);
1478                        dent->d_ino = 0;
1479                } else {
1480                        debug_printf("getdirentries:  allowed %s\n", dirname);
1481                }
1482                offset += dent->d_reclen;
1483        }
1484
1485        return sz;
1486#undef getdirentries
1487}
1488
1489int access(const char *path, int amode) {
1490#define access(x, y) syscall(SYS_access, (x), (y))
1491#define lstat(path, sb) syscall(SYS_lstat, path, sb)
1492        struct stat st;
1493        char newpath[MAXPATHLEN];
1494
1495        debug_printf("access(%s, %d)\n", path, amode);
1496
1497        if (-1 == lstat(path, &st)) {
1498                return -1;
1499        }
1500
1501        if (S_ISDIR(st.st_mode)) {
1502                return access(path, amode);
1503        }
1504
1505        *newpath = '\0';
1506        if (!__darwintrace_is_in_sandbox(path, newpath, true)) {
1507                errno = ENOENT;
1508                return -1;
1509        }
1510
1511        if (*newpath) {
1512                return access(newpath, amode);
1513        }
1514
1515        return access(path, amode);
1516#undef lstat
1517#undef access
1518}
1519
1520#endif /* __APPLE__ */
Note: See TracBrowser for help on using the repository browser.