[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