[107797] trunk/base/src/darwintracelib1.0/darwintrace.c
cal at macports.org
cal at macports.org
Fri Jul 5 15:13:44 PDT 2013
Revision: 107797
https://trac.macports.org/changeset/107797
Author: cal at macports.org
Date: 2013-07-05 15:13:44 -0700 (Fri, 05 Jul 2013)
Log Message:
-----------
darwintrace: complete overhaul
- lots of cleanup and removal of unused code
- switch to FILE *-based socket communication
- lots of API documentation
- remove always_inline in favor of letting the compiler decide what's best
- remove DARWINTRACE_DEBUG_LOG, which was a hassle to support and hardly used
- deal with files in $prefix, not registered to any port
- add lots of error handling
Modified Paths:
--------------
trunk/base/src/darwintracelib1.0/darwintrace.c
Modified: trunk/base/src/darwintracelib1.0/darwintrace.c
===================================================================
--- trunk/base/src/darwintracelib1.0/darwintrace.c 2013-07-05 21:30:11 UTC (rev 107796)
+++ trunk/base/src/darwintracelib1.0/darwintrace.c 2013-07-05 22:13:44 UTC (rev 107797)
@@ -78,7 +78,6 @@
#ifndef HAVE_STRLCPY
/* Define strlcpy if it's not available. */
-size_t strlcpy(char* dst, const char* src, size_t size);
size_t strlcpy(char* dst, const char* src, size_t size)
{
size_t result = strlen(src);
@@ -96,15 +95,7 @@
}
#endif
-/*
- * Compile time options:
- * DARWINTRACE_SHOW_PROCESS: show the process id of every access
- * DARWINTRACE_LOG_CREATE: log creation of files as well.
- * DARWINTRACE_SANDBOX: control creation, deletion and writing to files and dirs.
- * DARWINTRACE_LOG_FULL_PATH: use F_GETPATH to log the full path.
- * DARWINTRACE_DEBUG_OUTPUT: verbose output of stuff to debug darwintrace.
- *
- * global variables (only checked when setup is first called)
+/* global variables (only checked when setup is first called)
* DARWINTRACE_LOG
* path to the log file (no logging happens if it's unset).
* DARWINTRACE_SANDBOX_BOUNDS
@@ -113,29 +104,13 @@
* \\ -> \
*/
-#ifndef DARWINTRACE_SHOW_PROCESS
-#define DARWINTRACE_SHOW_PROCESS 0
+/*
+ * DARWINTRACE_DEBUG: verbose output of operations to debug darwintrace
+ */
+#ifndef DARWINTRACE_DEBUG
+#define DARWINTRACE_DEBUG (0)
#endif
-#ifndef DARWINTRACE_LOG_CREATE
-#define DARWINTRACE_LOG_CREATE 0
-#endif
-#ifndef DARWINTRACE_SANDBOX
-#define DARWINTRACE_SANDBOX 1
-#endif
-#ifndef DARWINTRACE_DEBUG_OUTPUT
-#define DARWINTRACE_DEBUG_OUTPUT 0
-#endif
-#ifndef DARWINTRACE_LOG_FULL_PATH
-#define DARWINTRACE_LOG_FULL_PATH 1
-#endif
-#ifndef DEFFILEMODE
-#define DEFFILEMODE 0666
-#endif
-
-/*
- * Prototypes.
- */
static inline int __darwintrace_strbeginswith(const char* str, const char* prefix);
static inline int __darwintrace_pathbeginswith(const char* str, const char* prefix);
static inline void __darwintrace_log_op(const char* op, const char* path, int fd);
@@ -144,50 +119,87 @@
static inline char* const* __darwintrace_restore_env(char* const envp[]);
static inline void __darwintrace_setup();
static inline void __darwintrace_cleanup_path(char *path);
-static char * exchange_with_port(const char * buf, size_t len, int answer);
+static char *__send(const char * buf, size_t len, int answer);
-static int __darwintrace_fd = -2;
-static FILE *__darwintrace_debug = NULL;
+/**
+ * Communication socket to the macports process, or NULL.
+ */
+static FILE *__darwintrace_sock = NULL;
+
+/**
+ * PID of the process darwintrace was last used in. This is used to detect
+ * forking and opening a new connection to the control socket in the child
+ * process. Not doing so would potentially cause two processes writing to the
+ * same socket.
+ */
static pid_t __darwintrace_pid = (pid_t) -1;
+
+/**
+ * Helper variable containing the number of the darwintrace socket, iff the
+ * close(2) syscall should be allowed to close it. Used by \c
+ * __darwintrace_close.
+ */
+static volatile int __darwintrace_close_sock = -1;
+
+/**
+ * size of the communication buffer
+ */
#define BUFFER_SIZE 1024
/**
- * filemap: path\0whattodo\0path\0whattodo\0\0
- * path: begin of path (for example /opt)
- * whattodo:
- * 0 -- allow
- * 1PATH -- map
- * 2 -- ask for allow
-**/
-static char * filemap=0;
+ * Variable holding the sandbox bounds in the following format:
+ * <filemap> :: (<spec> '\0')+ '\0'
+ * <spec> :: <path> '\0' <operation> <additional_data>?
+ * <operation> :: '0' | '1' | '2'
+ * where
+ * 0: allow
+ * 1: map the path to the one given in additional_data
+ * 2: check for a dependency using the socket
+ */
+static char *filemap;
-/* copy of the global variables */
+enum {
+ FILEMAP_ALLOW = 0,
+ FILEMAP_REDIR = 1,
+ FILEMAP_ASK = 2
+};
+
+/**
+ * Copy of the DYLD_INSERT_LIBRARIES environment variable to restore it in
+ * execve(2). DYLD_INSERT_LIBRARIES is needed to preload this library into any
+ * process' address space.
+ */
static char* __env_dyld_insert_libraries;
+
+/**
+ * Copy of the DYLD_FORCE_FLAT_NAMESPACE environment variable to restore it in
+ * execve(2). DYLD_FORCE_FLAT_NAMESPACE=1 is needed for the preload-based
+ * sandbox to work.
+ */
static char* __env_dyld_force_flat_namespace;
+
+/**
+ * Copy of the DARWINTRACE_LOG environment variable to restore it in execve(2).
+ * Contains the path to the unix socket used for communication with the
+ * MacPorts-side of the sandbox.
+ */
static char* __env_darwintrace_log;
-static char* __env_darwintrace_debug_log;
-#if DARWINTRACE_DEBUG_OUTPUT
-#if __STDC_VERSION__>=199901L
-#define debug_printf(format, ...) fprintf(stderr, "darwintrace[%d]: " format, getpid(), __VA_ARGS__); \
- if (__darwintrace_debug) { \
- fprintf(__darwintrace_debug, "darwintrace: " format, __VA_ARGS__); \
+#if DARWINTRACE_DEBUG
+# if __STDC_VERSION__>=199901L
+# define debug_printf(format, ...) \
+ fprintf(stderr, "darwintrace[%d]: " format, getpid(), __VA_ARGS__);
+# else
+ __attribute__((format(printf, 1, 2))) static inline void debug_printf(const char *format, ...) {
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
}
+# endif
#else
-__attribute__ ((format (printf, 1, 2)))
-static inline
-int debug_printf(const char *format, ...) {
- int ret;
- va_list args;
- va_start(args, format);
- ret = vfprintf(stderr, format, args);
- va_end(args);
- return ret;
-}
+# define debug_printf(...)
#endif
-#else
-#define debug_printf(...)
-#endif
/**
* Return 0 if str doesn't begin with prefix, 1 otherwise. Note that this is
@@ -198,7 +210,7 @@
char s;
char p;
- // '/' is the allow all wildcard
+ /* '/' is the allow all wildcard */
if (strcmp(prefix, "/") == 0) {
return 1;
}
@@ -213,7 +225,7 @@
/**
* Return 0 if str doesn't begin with prefix, 1 otherwise.
*/
-inline int __darwintrace_strbeginswith(const char* str, const char* prefix) {
+static inline int __darwintrace_strbeginswith(const char* str, const char* prefix) {
char s;
char p;
do {
@@ -224,142 +236,119 @@
}
/*
- * Copy the environment variables, if they're defined.
+ * Copy the environment variables, if they're defined. This is run as
+ * a constructor at startup.
*/
-void __darwintrace_copy_env() {
- char* theValue;
- theValue = getenv("DYLD_INSERT_LIBRARIES");
- if (theValue != NULL) {
- __env_dyld_insert_libraries = strdup(theValue);
- } else {
- __env_dyld_insert_libraries = NULL;
+static void __darwintrace_copy_env() {
+#define COPYENV(name, variable) \
+ if (NULL != (val = getenv(#name))) {\
+ if (NULL == (variable = strdup(val))) {\
+ perror("darwintrace: strdup");\
+ abort();\
+ }\
+ } else {\
+ variable = NULL;\
}
- theValue = getenv("DYLD_FORCE_FLAT_NAMESPACE");
- if (theValue != NULL) {
- __env_dyld_force_flat_namespace = strdup(theValue);
- } else {
- __env_dyld_force_flat_namespace = NULL;
- }
- theValue = getenv("DARWINTRACE_LOG");
- if (theValue != NULL) {
- __env_darwintrace_log = strdup(theValue);
- } else {
- __env_darwintrace_log = NULL;
- }
- theValue = getenv("DARWINTRACE_DEBUG_LOG");
- if (theValue != NULL) {
- __env_darwintrace_debug_log = strdup(theValue);
- } else {
- __env_darwintrace_debug_log = NULL;
- }
+
+ char* val;
+ COPYENV(DYLD_INSERT_LIBRARIES, __env_dyld_insert_libraries)
+ COPYENV(DYLD_FORCE_FLAT_NAMESPACE, __env_dyld_force_flat_namespace)
+ COPYENV(DARWINTRACE_LOG, __env_darwintrace_log)
+#undef COPYENV
}
-/*
+/**
* Allocate a X=Y string where X is the variable name and Y its value.
* Return the new string.
*
* If the value is NULL, return NULL.
*/
-static inline char* __darwintrace_alloc_env(const char* varName, const char* varValue) {
- char* theResult = NULL;
- if (varValue) {
- int theSize = strlen(varName) + strlen(varValue) + 2;
- theResult = (char*) malloc(theSize);
- if (theResult) {
- snprintf(theResult, theSize, "%s=%s", varName, varValue);
- theResult[theSize - 1] = 0;
+static inline char *__darwintrace_alloc_env(const char *name, const char *val) {
+ char *result = NULL;
+
+ if (val) {
+ size_t size = strlen(name) + strlen(val) + 2;
+ if (NULL == (result = malloc(size))) {
+ perror("darwintrace: malloc");
+ abort();
}
+ snprintf(result, size, "%s=%s", name, val);
+ if (size > 0) {
+ result[size - 1] = '\0';
+ }
}
- return theResult;
+ return result;
}
-/*
+/**
* This function checks that envp contains the global variables we had when the
* library was loaded and modifies it if it doesn't.
*/
-__attribute__((always_inline))
-static inline char* const* __darwintrace_restore_env(char* const envp[]) {
+static inline char *const *__darwintrace_restore_env(char *const envp[]) {
/* allocate the strings. */
/* we don't care about the leak here because we're going to call execve,
* which, if it succeeds, will get rid of our heap */
- char* dyld_insert_libraries_ptr =
- __darwintrace_alloc_env(
- "DYLD_INSERT_LIBRARIES",
- __env_dyld_insert_libraries);
- char* dyld_force_flat_namespace_ptr =
- __darwintrace_alloc_env(
- "DYLD_FORCE_FLAT_NAMESPACE",
- __env_dyld_force_flat_namespace);
- char* darwintrace_log_ptr =
- __darwintrace_alloc_env(
- "DARWINTRACE_LOG",
- __env_darwintrace_log);
- char* darwintrace_debug_log_ptr =
- __darwintrace_alloc_env(
- "DARWINTRACE_DEBUG_LOG",
- __env_darwintrace_debug_log);
+ char *dyld_insert_libraries_ptr = __darwintrace_alloc_env("DYLD_INSERT_LIBRARIES", __env_dyld_insert_libraries);
+ char *dyld_force_flat_namespace_ptr = __darwintrace_alloc_env("DYLD_FORCE_FLAT_NAMESPACE", __env_dyld_force_flat_namespace);
+ char *darwintrace_log_ptr = __darwintrace_alloc_env("DARWINTRACE_LOG", __env_darwintrace_log);
- char* const * theEnvIter = envp;
- int theEnvLength = 0;
- char** theCopy;
- char** theCopyIter;
+ char *const *enviter = envp;
+ size_t envlen = 0;
+ char **copy;
+ char **copyiter;
- while (*theEnvIter != NULL) {
- theEnvLength++;
- theEnvIter++;
+ while (*enviter != NULL) {
+ envlen++;
+ enviter++;
}
- /* 5 is sufficient for the four variables we copy and the terminator */
- theCopy = (char**) malloc(sizeof(char*) * (theEnvLength + 5));
- theEnvIter = envp;
- theCopyIter = theCopy;
+ /* 4 is sufficient for the three variables we copy and the terminator */
+ copy = malloc(sizeof(char *) * (envlen + 5));
- while (*theEnvIter != NULL) {
- char* theValue = *theEnvIter;
- if (__darwintrace_strbeginswith(theValue, "DYLD_INSERT_LIBRARIES=")) {
- theValue = dyld_insert_libraries_ptr;
+ enviter = envp;
+ copyiter = copy;
+
+ while (*enviter != NULL) {
+ char *val = *enviter;
+ if (__darwintrace_strbeginswith(val, "DYLD_INSERT_LIBRARIES=")) {
+ val = dyld_insert_libraries_ptr;
dyld_insert_libraries_ptr = NULL;
- } else if (__darwintrace_strbeginswith(theValue, "DYLD_FORCE_FLAT_NAMESPACE=")) {
- theValue = dyld_force_flat_namespace_ptr;
+ } else if (__darwintrace_strbeginswith(val, "DYLD_FORCE_FLAT_NAMESPACE=")) {
+ val = dyld_force_flat_namespace_ptr;
dyld_force_flat_namespace_ptr = NULL;
- } else if (__darwintrace_strbeginswith(theValue, "DARWINTRACE_LOG=")) {
- theValue = darwintrace_log_ptr;
+ } else if (__darwintrace_strbeginswith(val, "DARWINTRACE_LOG=")) {
+ val = darwintrace_log_ptr;
darwintrace_log_ptr = NULL;
- } else if (__darwintrace_strbeginswith(theValue, "DARWINTRACE_DEBUG_LOG=")) {
- theValue = darwintrace_debug_log_ptr;
- darwintrace_debug_log_ptr = NULL;
}
- if (theValue) {
- *theCopyIter++ = theValue;
+ if (val) {
+ *copyiter++ = val;
}
- theEnvIter++;
+ enviter++;
}
if (dyld_insert_libraries_ptr) {
- *theCopyIter++ = dyld_insert_libraries_ptr;
+ *copyiter++ = dyld_insert_libraries_ptr;
}
if (dyld_force_flat_namespace_ptr) {
- *theCopyIter++ = dyld_force_flat_namespace_ptr;
+ *copyiter++ = dyld_force_flat_namespace_ptr;
}
if (darwintrace_log_ptr) {
- *theCopyIter++ = darwintrace_log_ptr;
+ *copyiter++ = darwintrace_log_ptr;
}
- if (darwintrace_debug_log_ptr) {
- *theCopyIter++ = darwintrace_debug_log_ptr;
- }
- *theCopyIter = 0;
+ *copyiter = 0;
- return theCopy;
+ return copy;
}
/*
* Data structures and functions to iterate over the filemap received from
* tracelib code.
*/
+
/**
* \c filemap_iterator_t is an (opaque) iterator type that keeps the state
* required to iterate through the filemap. Create a new filemap_iterator_t on
@@ -376,7 +365,6 @@
*
* \param[in] it pointer to the iterator to be initialized
*/
-__attribute__((always_inline))
static inline void __darwintrace_filemap_iterator_init(filemap_iterator_t *it) {
it->next = filemap;
}
@@ -396,7 +384,6 @@
* \return string containing the path this filemap entry corresponds to, or \c
* NULL if the end of the filemap was reached
*/
-__attribute__((always_inline))
static inline char *__darwintrace_filemap_iter(char *command, char **replacement, filemap_iterator_t *it) {
enum { PATH, COMMAND, REPLACEPATH, DONE } state = PATH;
char *t;
@@ -454,82 +441,107 @@
* of the trace setup) and store it.
*/
static void __darwintrace_get_filemap() {
- filemap = exchange_with_port("filemap\t", sizeof("filemap\t"), 1);
+#if DARWINTRACE_DEBUG
+ filemap_iterator_t it;
+ char *path, *replacement, command;
+#endif
+
+ /* if this is called multiple times, free the old value; on first call,
+ * filemap == NULL */
+ free(filemap);
+ filemap = __send("filemap\t", sizeof("filemap\t"), 1);
if (filemap == (char*) -1)
filemap = 0;
-# if 0
- if (DARWINTRACE_DEBUG_OUTPUT) {
- filemap_iterator_t it;
- char *path, *replacement, command;
+#if DARWINTRACE_DEBUG
+ for (__darwintrace_filemap_iterator_init(&it);
+ (path = __darwintrace_filemap_iter(&command, &replacement, &it));) {
+ debug_printf("filemap: {cmd=%d, path=%-120s, replacement=%s}\n", command, path, (command == 1) ? replacement : "-");
+ }
+#endif
+}
- for (__darwintrace_filemap_iterator_init(&it);
- (path = __darwintrace_filemap_iter(&command, &replacement, &it));) {
- debug_printf("filemap: {cmd=%d, path=%-120s, replacement=%s}\n", command, path, (command == 1) ? replacement : "-");
- }
+/**
+ * Close the stream at location \c *stream and set \c stream to \c NULL. Since
+ * this uses \c fclose(3), which internally calls \c close(2), which is
+ * intercepted by this library and this library prevents closing the socket to
+ * MacPorts, we use \c __darwintrace_close_sock to allow closing specific FDs.
+ *
+ * \param[inout] stream pointer to a stream to be closed. Will be set to \c
+ * NULL.
+ */
+static inline void __darwintrace_close(FILE **stream) {
+ if (stream && *stream) {
+ __darwintrace_close_sock = fileno(*stream);
+ fclose(*stream);
+ *stream = NULL;
+ __darwintrace_close_sock = -1;
}
-# endif
}
-__attribute__((always_inline))
+/**
+ * Ensures darwintrace is correctly set up by opening a socket connection to
+ * the MacPorts-side of trace mode. Will close an re-open this connection when
+ * called after \c fork(2), i.e. when the current PID doesn't match the one
+ * stored when the function was called last.
+ */
static inline void __darwintrace_setup() {
-#define open(x,y,z) syscall(SYS_open, (x), (y), (z))
-#define close(x) syscall(SYS_close, (x))
- pid_t oldpid = __darwintrace_pid;
+ /**
+ * Check whether this is a child process and we've inherited the socket. We
+ * want to avoid race conditions with our parent process when communicating
+ * with tracelib and thus re-open all sockets, if that's the case.
+ */
if (__darwintrace_pid != (pid_t) -1 && __darwintrace_pid != getpid()) {
- if (__darwintrace_fd != -2) {
- close(__darwintrace_fd);
- __darwintrace_fd = -2;
- }
- if (__darwintrace_debug) {
- fclose(__darwintrace_debug);
- __darwintrace_debug = NULL;
- }
+ __darwintrace_close(&__darwintrace_sock);
__darwintrace_pid = (pid_t) -1;
}
+
if (__darwintrace_pid == (pid_t) -1) {
+ int sock;
+ struct sockaddr_un sun;
+
__darwintrace_pid = getpid();
- if (__env_darwintrace_log != NULL) {
- int olderrno = errno;
- int sock = socket(AF_UNIX, SOCK_STREAM, 0);
- struct sockaddr_un sun;
- sun.sun_family = AF_UNIX;
- strncpy(sun.sun_path, __env_darwintrace_log, sizeof(sun.sun_path));
- if (connect(sock, (struct sockaddr*)&sun, strlen(__env_darwintrace_log) + 1 + sizeof(sun.sun_family)) != -1) {
- __darwintrace_fd = sock;
- __darwintrace_get_filemap();
- } else {
- debug_printf("connect failed: %s\n", strerror(errno));
- abort();
- }
- errno = olderrno;
+ if (__env_darwintrace_log == NULL) {
+ fprintf(stderr, "darwintrace: trace library loaded, but DARWINTRACE_LOG not set\n");
+ abort();
}
- if (__darwintrace_debug == NULL) {
- if (__env_darwintrace_debug_log != NULL) {
- char logpath[MAXPATHLEN];
- snprintf(logpath, MAXPATHLEN, __env_darwintrace_debug_log, getpid());
- if (NULL == (__darwintrace_debug = fopen(logpath, "w"))) {
- fprintf(stderr, "failed to open logfile: %s\n", strerror(errno));
- abort();
- }
- fprintf(__darwintrace_debug, "pid %d is process %s\n", getpid(), getenv("_"));
- debug_printf("logging socket communication to: %s\n", logpath);
- }
+
+ if (-1 == (sock = socket(PF_LOCAL, SOCK_STREAM, 0))) {
+ perror("darwintrace: socket");
+ abort();
}
- if (oldpid != (pid_t) -1) {
- debug_printf("seems to have forked from %d, re-opened files\n", oldpid);
+
+ if (strlen(__env_darwintrace_log) > sizeof(sun.sun_path) - 1) {
+ fprintf(stderr, "darwintrace: Can't connect to socket %s: name too long\n", __env_darwintrace_log);
+ abort();
}
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, __env_darwintrace_log, sizeof(sun.sun_path));
+ sun.sun_len = SUN_LEN(&sun) + 1;
+
+ if (-1 == (connect(sock, (struct sockaddr *) &sun, sun.sun_len))) {
+ perror("darwintrace: connect");
+ abort();
+ }
+
+ if (NULL == (__darwintrace_sock = fdopen(sock, "a+"))) {
+ perror("darwintrace: fdopen");
+ abort();
+ }
+
+ /* request sandbox bounds */
+ __darwintrace_get_filemap();
}
-#undef close
-#undef open
}
-/* log a call and optionally get the real path from the fd if it's not 0.
- * op: the operation (open, readlink, execve)
- * path: the path of the file
- * fd: a fd to the file, or 0 if we don't have any.
+/**
+ * Send a path to tracelib either form a given path, or from an FD.
+ *
+ * \param[in] op the operation (sent as-is to tracelib, should be interpreted
+ * as command)
+ * \param[in] path the (not necessarily absolute) path to send to tracelib
+ * \param[in] fd a FD to the file, or 0, if none available
*/
-__attribute__((always_inline))
static inline void __darwintrace_log_op(const char* op, const char* path, int fd) {
int size;
char somepath[MAXPATHLEN];
@@ -537,7 +549,7 @@
do {
# ifdef __APPLE__ /* Only Darwin has volfs and F_GETPATH */
- if ((fd > 0) && (DARWINTRACE_LOG_FULL_PATH || (strncmp(path, "/.vol/", 6) == 0))) {
+ if ((fd > 0) && (strncmp(path, "/.vol/", 6) == 0)) {
if (fcntl(fd, F_GETPATH, somepath) != -1) {
break;
}
@@ -563,13 +575,11 @@
__darwintrace_cleanup_path(somepath);
size = snprintf(logbuffer, sizeof(logbuffer), "%s\t%s", op, somepath);
-
- exchange_with_port(logbuffer, size + 1, 0);
-
- return;
+ __send(logbuffer, size + 1, 0);
}
-/* remap resource fork access to the data fork.
+/**
+ * remap resource fork access to the data fork.
* do a partial realpath(3) to fix "foo//bar" to "foo/bar"
*/
static inline void __darwintrace_cleanup_path(char *path) {
@@ -577,7 +587,7 @@
# ifdef __APPLE__
size_t rsrclen;
# endif
- size_t i, shiftamount;
+ char *dst, *src;
enum { SAWSLASH, NOTHING } state = NOTHING;
/* if this is a foo/..namedfork/rsrc, strip it off */
@@ -591,36 +601,47 @@
}
# endif
- /* for each position in string (including terminal \0), check if we're in
- * a run of multiple slashes, and only emit the first one */
- for(i = 0, shiftamount = 0; i <= pathlen; i++) {
+ /* for each position in string, check if we're in a run of multiple
+ * slashes, and only emit the first one */
+ for (src = path, dst = path; *src; src++) {
if (state == SAWSLASH) {
- if (path[i] == '/') {
+ if (*src == '/') {
/* consume it */
- shiftamount++;
continue;
- } else {
- state = NOTHING;
}
+ state = NOTHING;
} else {
- if (path[i] == '/') {
+ if (*src == '/') {
state = SAWSLASH;
}
}
- path[i - shiftamount] = path[i];
+ if (dst != src) {
+ // if dst == src, avoid the copy operation
+ *dst = *src;
+ }
+ dst++;
}
}
-/*
- * return 1 if path allowed, 0 otherwise
+/**
+ * Check whether the port currently being installed declares a dependency on
+ * a given file. Communicates with MacPorts tracelib, which uses the registry
+ * database to answer this question. Returns 1, if a dependency was declared,
+ * 0, if the file belongs to a port and no dependency was declared and -1 if
+ * the file isnt't registered to any port.
+ *
+ * \param[in] path the path to send to MacPorts for dependency info
+ * \return 1, if access should be granted, 0, if access should be denied, and
+ * -1 if MacPorts doesn't know about the file.
*/
-static int ask_for_dependency(char * path) {
+static int dependency_check(char *path) {
#define stat(y, z) syscall(SYS_stat, (y), (z))
char buffer[BUFFER_SIZE], *p;
+ size_t len;
int result = 0;
struct stat st;
- debug_printf("ask_for_dependency: %s\n", path);
+ debug_printf("dependency_check: %s\n", path);
if (-1 == stat(path, &st)) {
return 1;
@@ -630,94 +651,119 @@
return 1;
}
- strncpy(buffer, "dep_check\t", sizeof(buffer));
- strncpy(buffer+10, path, sizeof(buffer)-10);
- p=exchange_with_port(buffer, strlen(buffer)+1, 1);
- if(p==(char*)-1||!p)
- return 0;
-
- if(*p=='+')
- result=1;
-
+ len = snprintf(buffer, sizeof(buffer), "dep_check\t%s", path);
+ if (len + 1 > sizeof(buffer)) {
+ len = sizeof(buffer) - 1;
+ }
+ p = __send(buffer, len + 1, 1);
+ if (!p) {
+ fprintf(stderr, "darwintrace: dependency check failed for %s\n", path);
+ abort();
+ }
+
+ switch (*p) {
+ case '+':
+ result = 1;
+ break;
+ case '!':
+ result = 0;
+ break;
+ case '?':
+ result = -1;
+ break;
+ default:
+ fprintf(stderr, "darwintrace: unexpected answer from tracelib: %c", *p);
+ abort();
+ break;
+ }
+
free(p);
return result;
#undef stat
}
-/*
- * exchange_with_port - routine to send/recv from/to socket
- * Parameters:
- * buf -- buffer with data to send
- * len -- length of data
- * answer -- 1 (yes, I want to receive answer) and 0 (no, thanks, just send)
- * failures -- should be setted 0 on external calls (avoid infinite recursion)
- * Return value:
- * -1 -- something went wrong
- * 0 -- data successfully sent
- * string -- answer (caller shoud free it)
+/**
+ * Helper function to recieve a number of bytes from the tracelib communication
+ * socket and deal with any errors that might occur.
+ *
+ * \param[out] buf buffer to hold received data
+ * \param[in] size number of bytes to read from the socket
*/
-static char * exchange_with_port(const char * buf, size_t len, int answer) {
- size_t sent = 0;
+static void frecv(void *restrict buf, size_t size) {
+ if (1 != fread(buf, size, 1, __darwintrace_sock)) {
+ if (ferror(__darwintrace_sock)) {
+ perror("darwintrace: fread");
+ } else {
+ fprintf(stderr, "darwintrace: fread: end-of-file\n");
+ }
+ abort();
+ }
+}
- if (__darwintrace_debug) {
- fprintf(__darwintrace_debug, "> %s\n", buf);
- }
- while (sent < len) {
- ssize_t local_sent = send(__darwintrace_fd, buf + sent, len - sent, 0);
- if (local_sent == -1) {
- debug_printf("error communicating with socket %d: %s\n", __darwintrace_fd, strerror(errno));
- if (__darwintrace_debug)
- fprintf(__darwintrace_debug, "darwintrace: error communicating with socket %d: %s\n", __darwintrace_fd, strerror(errno));
- abort();
+/**
+ * Helper function to send a buffer to MacPorts using the tracelib
+ * communication socket and deal with any errors that might occur.
+ *
+ * \param[in] buf buffer to send
+ * \param[in] size number of bytes in the buffer
+ */
+static void fsend(const void *restrict buf, size_t size) {
+ if (1 != fwrite(buf, size, 1, __darwintrace_sock)) {
+ if (ferror(__darwintrace_sock)) {
+ perror("darwintrace: fwrite");
+ } else {
+ fprintf(stderr, "darwintrace: fwrite: end-of-file\n");
}
- sent += local_sent;
+ abort();
}
+ fflush(__darwintrace_sock);
+}
+
+/**
+ * Communication wrapper targeting tracelib. Automatically enforces the on-wire
+ * protocol and supports reading and returning an answer.
+ *
+ * \param[in] buf buffer to send to tracelib
+ * \param[in] size size of the buffer to send
+ * \param[in] answer boolean indicating whether an answer is expected and
+ * should be returned
+ * \return allocated answer buffer. Callers should free this buffer. If an
+ * answer was not requested, \c NULL.
+ */
+static char *__send(const char *buf, size_t len, int answer) {
+ fsend(buf, len);
+
if (!answer) {
+ return NULL;
+ }
+
+ size_t recv_len = 0;
+ char *recv_buf;
+
+ frecv(&recv_len, sizeof(recv_len));
+ if (recv_len == 0) {
return 0;
- } else {
- size_t recv_len = 0, received;
- char *recv_buf;
-
- received = 0;
- while (received < sizeof(recv_len)) {
- ssize_t local_received = recv(__darwintrace_fd, ((char *) &recv_len) + received, sizeof(recv_len) - received, 0);
- if (local_received == -1) {
- debug_printf("error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
- if (__darwintrace_debug)
- fprintf(__darwintrace_debug, "darwintrace: error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
- abort();
- }
- received += local_received;
- }
- if (recv_len == 0) {
- return 0;
- }
+ }
- recv_buf = malloc(recv_len + 1);
- recv_buf[recv_len] = '\0';
+ recv_buf = malloc(recv_len + 1);
+ recv_buf[recv_len] = '\0';
+ frecv(recv_buf, recv_len);
- received = 0;
- while (received < recv_len) {
- ssize_t local_received = recv(__darwintrace_fd, recv_buf + received, recv_len - received, 0);
- if (local_received == -1) {
- debug_printf("error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
- if (__darwintrace_debug)
- fprintf(__darwintrace_debug, "darwintrace: error reading data from socket %d: %s\n", __darwintrace_fd, strerror(errno));
- abort();
- }
- received += local_received;
- }
- if (__darwintrace_debug) {
- fprintf(__darwintrace_debug, "< %s\n", recv_buf);
- }
- return recv_buf;
- }
+ return recv_buf;
}
-/*
- * return 1 if path (once normalized) is in sandbox or redirected, 0 otherwise.
+/**
+ * Check a path against the current sandbox
+ *
+ * \param[in] path the path to be checked; not necessarily absolute
+ * \param[out] newpath buffer for a replacement path when redirection should
+ * occur. Initialize the first byte with 0 before calling
+ * this function. The buffer should be at least MAXPATHLEN
+ * bytes large. If newpath[0] isn't 0 after the call,
+ * redirection should occur and the path from newpath
+ * should be used for the syscall instead.
+ * \return 1, if the file is within sandbox bounds, 0, if access should be denied
*/
-__attribute__((always_inline))
static inline int __darwintrace_is_in_sandbox(const char* path, char * newpath) {
char *t, *_;
char *strpos, *normpos;
@@ -786,9 +832,9 @@
/* move t to the integer describing how to handle this match */
t += strlen(t) + 1;
switch (*t) {
- case 0:
+ case FILEMAP_ALLOW:
return 1;
- case 1:
+ case FILEMAP_REDIR:
if (!newpath) {
return 0;
}
@@ -798,17 +844,21 @@
_ = newpath + strlen(newpath);
/* append '/' if it's missing */
if (_[-1] != '/') {
- *_ = '/';
+ *_++ = '/';
}
strcpy(_, normalizedpath);
return 1;
- case 2:
+ case FILEMAP_ASK:
/* ask the socket whether this file is OK */
- if (ask_for_dependency(normalizedpath)) {
- return 1;
- } else {
- /* try to find another entry allowing this access */
- continue;
+ switch (dependency_check(normalizedpath)) {
+ case 1:
+ case -1:
+ /* if the file isn't known to MacPorts, allow
+ * access anyway. TODO find a better solution */
+ return 1;
+ case 0:
+ /* file belongs to a foreign port, deny access */
+ return 0;
}
default:
fprintf(stderr, "darwintrace: error: unexpected byte in file map: `%x'\n", *t);
@@ -885,72 +935,64 @@
#define lstat(x, y) syscall(SYS_lstat, (x), (y))
debug_printf("execve(%s)\n", path);
__darwintrace_setup();
- if (__darwintrace_fd >= 0) {
- struct stat sb;
- /* for symlinks, we want to capture both the original path and the
- * modified one, since for /usr/bin/gcc -> gcc-4.0, both "gcc_select"
- * and "gcc" are contributors
- */
- if (lstat(path, &sb) == 0) {
- int fd;
- if (S_ISLNK(sb.st_mode)) {
- /* for symlinks, print both */
- __darwintrace_log_op("execve", path, 0);
- }
+ struct stat sb;
+ /* for symlinks, we want to capture both the original path and the
+ * modified one, since for /usr/bin/gcc -> gcc-4.0, both "gcc_select"
+ * and "gcc" are contributors
+ */
+ if (lstat(path, &sb) == 0) {
+ int fd;
+ if (S_ISLNK(sb.st_mode)) {
+ /* for symlinks, print both */
+ __darwintrace_log_op("execve", path, 0);
+ }
- fd = open(path, O_RDONLY, 0);
- if (fd > 0) {
- char buffer[MAXPATHLEN+1];
- ssize_t bytes_read;
+ fd = open(path, O_RDONLY, 0);
+ if (fd > 0) {
+ char buffer[MAXPATHLEN+1];
+ ssize_t bytes_read;
- if(!__darwintrace_is_in_sandbox(path, NULL)) {
- close(fd);
- errno = ENOENT;
- return -1;
- }
+ if(!__darwintrace_is_in_sandbox(path, NULL)) {
+ close(fd);
+ errno = ENOENT;
+ return -1;
+ }
- /* once we have an open fd, if a full path was requested, do it */
- __darwintrace_log_op("execve", path, fd);
+ /* once we have an open fd, if a full path was requested, do it */
+ __darwintrace_log_op("execve", path, fd);
- /* read the file for the interpreter */
- bytes_read = read(fd, buffer, MAXPATHLEN);
- buffer[bytes_read] = 0;
- if (bytes_read > 2 && buffer[0] == '#' && buffer[1] == '!') {
- const char* interp = &buffer[2];
- int i;
- /* skip past leading whitespace */
- for (i = 2; i < bytes_read; ++i) {
- if (buffer[i] != ' ' && buffer[i] != '\t') {
- interp = &buffer[i];
- break;
- }
+ /* read the file for the interpreter */
+ bytes_read = read(fd, buffer, MAXPATHLEN);
+ buffer[bytes_read] = 0;
+ if (bytes_read > 2 && buffer[0] == '#' && buffer[1] == '!') {
+ const char* interp = &buffer[2];
+ int i;
+ /* skip past leading whitespace */
+ for (i = 2; i < bytes_read; ++i) {
+ if (buffer[i] != ' ' && buffer[i] != '\t') {
+ interp = &buffer[i];
+ break;
}
- /* found interpreter (or ran out of data); skip until next
- * whitespace, then terminate the string */
- for (; i < bytes_read; ++i) {
- if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n') {
- buffer[i] = 0;
- break;
- }
+ }
+ /* found interpreter (or ran out of data); skip until next
+ * whitespace, then terminate the string */
+ for (; i < bytes_read; ++i) {
+ if (buffer[i] == ' ' || buffer[i] == '\t' || buffer[i] == '\n') {
+ buffer[i] = 0;
+ break;
}
- /* we have liftoff */
- if (interp && interp[0] != '\0') {
- __darwintrace_log_op("execve", interp, 0);
- }
}
- close(fd);
+ /* we have liftoff */
+ if (interp && interp[0] != '\0') {
+ __darwintrace_log_op("execve", interp, 0);
+ }
}
+ close(fd);
}
}
+
/* our variables won't survive exec, clean up */
- if (__darwintrace_fd != -2) {
- close(__darwintrace_fd);
- __darwintrace_fd = -2;
- }
- if (__darwintrace_debug) {
- fclose(__darwintrace_debug);
- __darwintrace_debug = NULL;
- }
+ __darwintrace_close(&__darwintrace_sock);
__darwintrace_pid = (pid_t) -1;
/* call the original execve function, but fix the environment if required. */
@@ -965,9 +1007,12 @@
* descriptor */
int close(int fd) {
#define close(x) syscall(SYS_close, (x))
- if (__darwintrace_fd != -2 && fd == __darwintrace_fd) {
- errno = EBADF;
- return -1;
+ if (__darwintrace_sock) {
+ int dtsock = fileno(__darwintrace_sock);
+ if (fd == dtsock && dtsock != __darwintrace_close_sock) {
+ errno = EBADF;
+ return -1;
+ }
}
return close(fd);
@@ -979,26 +1024,29 @@
#define dup2(x, y) syscall(SYS_dup2, (x), (y))
debug_printf("dup2(%d, %d)\n", filedes, filedes2);
- if (__darwintrace_fd != -2 && filedes2 == __darwintrace_fd) {
+ if (__darwintrace_sock && filedes2 == fileno(__darwintrace_sock)) {
/* if somebody tries to close our file descriptor, just move it out of
* the way. Make sure it doesn't end up as stdin/stdout/stderr, though!
* */
int new_darwintrace_fd;
- if (-1 == (new_darwintrace_fd = fcntl(__darwintrace_fd, F_DUPFD, STDOUT_FILENO + 1))) {
+ if (-1 == (new_darwintrace_fd = fcntl(fileno(__darwintrace_sock), F_DUPFD, STDOUT_FILENO + 1))) {
/* if duplicating fails, do not allow overwriting either! */
return -1;
}
- debug_printf("moving __darwintrace_fd from %d to %d\n", __darwintrace_fd, new_darwintrace_fd);
- __darwintrace_fd = new_darwintrace_fd;
+ debug_printf("moving __darwintrace FD from %d to %d\n", fileno(__darwintrace_sock), new_darwintrace_fd);
+ __darwintrace_close(&__darwintrace_sock);
+ if (NULL == (__darwintrace_sock = fdopen(new_darwintrace_fd, "a+"))) {
+ perror("darwintrace: fdopen");
+ abort();
+ }
}
return dup2(filedes, filedes2);
#undef dup2
}
-
/* Trap attempts to unlink a file outside the sandbox. */
int unlink(const char* path) {
#define __unlink(x) syscall(SYS_unlink, (x))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macports-changes/attachments/20130705/59a1f2f5/attachment-0001.html>
More information about the macports-changes
mailing list