Index: appfsd.c
==================================================================
--- appfsd.c
+++ appfsd.c
@@ -23,11 +23,29 @@
 #define APPFS_CACHEDIR "/var/cache/appfs"
 #endif
 
 /* Debugging macros */
 #ifdef DEBUG
-#define APPFS_DEBUG(x...) { fprintf(stderr, "[debug] %s:%i:%s: ", __FILE__, __LINE__, __func__); fprintf(stderr, x); fprintf(stderr, "\n"); }
+int appfs_debug_fd = STDERR_FILENO;
+#define APPFS_DEBUG(x...) { \
+	char buf[8192]; \
+	int bufoff = 0; \
+	if (appfs_debug_fd == -1) { \
+		appfs_debug_fd = open("/tmp/appfsd.log", O_WRONLY | O_APPEND | O_CREAT, 0600); \
+	}; \
+	bufoff = snprintf(buf, sizeof(buf), "[debug] [t=%llx] %s:%i:%s: ", (unsigned long long) pthread_self(), __FILE__, __LINE__, __func__); \
+	if (bufoff < sizeof(buf)) { \
+		bufoff += snprintf(buf + bufoff, sizeof(buf) - bufoff, x); \
+	}; \
+	if (bufoff < sizeof(buf)) { \
+		bufoff += snprintf(buf + bufoff, sizeof(buf) - bufoff, "\n");\
+	} \
+	if (bufoff > sizeof(buf)) { \
+		bufoff = sizeof(buf); \
+	}; \
+	write(appfs_debug_fd, buf, bufoff); \
+}
 #else
 #define APPFS_DEBUG(x...) /**/
 #endif
 
 /*
@@ -61,10 +79,11 @@
  */
 pthread_mutex_t appfs_tcl_big_global_lock = PTHREAD_MUTEX_INITIALIZER;
 #define appfs_call_libtcl_enter pthread_mutex_lock(&appfs_tcl_big_global_lock);
 #define appfs_call_libtcl_exit pthread_mutex_unlock(&appfs_tcl_big_global_lock);
 #else
+#warning Using a Threaded Tcl interpreter may cause memory leaks
 #define appfs_call_libtcl_enter /**/
 #define appfs_call_libtcl_exit /**/
 #endif
 #define appfs_call_libtcl(x...) appfs_call_libtcl_enter x appfs_call_libtcl_exit
 
@@ -379,10 +398,12 @@
 
 	if (interp == NULL) {
 		interp = appfs_create_TclInterp(NULL);
 
 		if (interp == NULL) {
+			APPFS_DEBUG("Create interp failed, returningin failure.");
+
 			return(NULL);
 		}
 
 		pthread_ret = pthread_setspecific(interpKey, interp);
 		if (pthread_ret != 0) {
@@ -407,10 +428,12 @@
 	va_list argp;
 	int retval;
 	int i;
 
 	if (interp == NULL) {
+		APPFS_DEBUG("Invalid interpreter passed in, returning in failure.");
+
 		return(TCL_ERROR);
 	}
 
 	objv = (void *) ckalloc(sizeof(*objv) * objc);
 
@@ -476,10 +499,12 @@
 
 	ctx = fuse_get_context();
 	if (ctx == NULL) {
 		/* Unable to lookup user for some reason */
 		/* Return an unprivileged user ID */
+		APPFS_DEBUG("Unable to lookup user for some reason, returninng user ID of 1");
+
 		return(1);
 	}
 
 	return(ctx->uid);
 }
@@ -498,10 +523,12 @@
 
 	ctx = fuse_get_context();
 	if (ctx == NULL) {
 		/* Unable to lookup user for some reason */
 		/* Return an unprivileged user ID */
+		APPFS_DEBUG("Unable to lookup group for some reason, returninng group ID of 1");
+
 		return(1);
 	}
 
 	return(ctx->gid);
 }
@@ -672,10 +699,11 @@
 	if (pthread_ret != 0) {
 		APPFS_DEBUG("Unable to unlock path_info cache mutex !");
 
 		return;
 	}
+
 	return;
 }
 
 static void appfs_get_path_info_cache_rm(const char *path, uid_t uid) {
 	unsigned int hash_idx;
@@ -775,22 +803,28 @@
 	fsuid = appfs_get_fsuid();
 
 	cache_ret = appfs_get_path_info_cache_get(path, fsuid, pathinfo);
 	if (cache_ret == 0) {
 		if (pathinfo->type == APPFS_PATHTYPE_DOES_NOT_EXIST) {
+			APPFS_DEBUG("Returning from cache: does not exist \"%s\"", path);
+
 			return(-ENOENT);
 		}
 
 		if (pathinfo->type == APPFS_PATHTYPE_INVALID) {
+			APPFS_DEBUG("Returning from cache: invalid object \"%s\"", path);
+
 			return(-EIO);
 		}
 
 		return(0);
 	}
 
 	interp = appfs_TclInterp();
 	if (interp == NULL) {
+		APPFS_DEBUG("error: Unable to get an interpreter");
+
 		return(-EIO);
 	}
 
 	appfs_call_libtcl(Tcl_Preserve(interp);)
 
@@ -844,10 +878,12 @@
 
 		return(-EIO);
 	}
 
 	if (attr_value == NULL) {
+		APPFS_DEBUG("error: Unable to get type for \"%s\" from Tcl", path);
+
 		appfs_call_libtcl(Tcl_Release(interp);)
 
 		return(-EIO);
 	}
 
@@ -937,10 +973,12 @@
 		Tcl_Release(interp);
 	)
 
 	if (retval == 0) {
 		appfs_get_path_info_cache_add(path, fsuid, pathinfo);
+	} else {
+		APPFS_DEBUG("error: Invalid type for \"%s\" from Tcl", path);
 	}
 
 	return(retval);
 }
 
@@ -1093,10 +1131,16 @@
 
 	pathinfo.type = APPFS_PATHTYPE_INVALID;
 
 	retval = appfs_get_path_info(path, &pathinfo);
 	if (retval != 0) {
+		if (retval == -ENOENT) {
+			APPFS_DEBUG("get_path_info returned ENOENT, returning it as well.");
+		} else {
+			APPFS_DEBUG("error: get_path_info failed");
+		}
+
 		return(retval);
 	}
 
 	memset(stbuf, 0, sizeof(struct stat));
 
@@ -1163,10 +1207,12 @@
 
 	APPFS_DEBUG("Enter (path = %s, ...)", path);
 
 	interp = appfs_TclInterp();
 	if (interp == NULL) {
+		APPFS_DEBUG("error: Unable to get an interpreter");
+
 		return(0);
 	}
 
 	appfs_call_libtcl(Tcl_Preserve(interp);)
 
@@ -1222,10 +1268,12 @@
 	gpi_ret = appfs_get_path_info(path, &pathinfo);
 
 	if ((fi->flags & (O_WRONLY|O_CREAT)) == (O_CREAT|O_WRONLY)) {
 		/* The file will be created if it does not exist */
 		if (gpi_ret != 0 && gpi_ret != -ENOENT) {
+			APPFS_DEBUG("error: get_path_info failed");
+
 			return(gpi_ret);
 		}
 
 		mode = "create";
 
@@ -1235,10 +1283,12 @@
 		 */
 		appfs_get_path_info_cache_flush(appfs_get_fsuid(), -1);
 	} else {
 		/* The file must already exist */
 		if (gpi_ret != 0) {
+			APPFS_DEBUG("error: get_path_info failed");
+
 			return(gpi_ret);
 		}
 
 		mode = "";
 
@@ -1246,15 +1296,19 @@
 			mode = "write";
 		}
 	}
 
 	if (pathinfo.type == APPFS_PATHTYPE_DIRECTORY) {
+		APPFS_DEBUG("error: Asked to open a directory.");
+
 		return(-EISDIR);
 	}
 
 	interp = appfs_TclInterp();
 	if (interp == NULL) {
+		APPFS_DEBUG("error: Unable to get an interpreter");
+
 		return(-EIO);
 	}
 
 	appfs_call_libtcl(Tcl_Preserve(interp);)
 
@@ -1275,19 +1329,23 @@
 	)
 
 	appfs_call_libtcl(Tcl_Release(interp);)
 
 	if (real_path == NULL) {
+		APPFS_DEBUG("error: real_path was NULL.")
+
 		return(-EIO);
 	}
 
 	APPFS_DEBUG("Translated request to open %s to opening %s (mode = \"%s\")", path, real_path, mode);
 
 	fh = open(real_path, fi->flags, 0600);
 
 	if (fh < 0) {
-		return(-EIO);
+		APPFS_DEBUG("error: open failed");
+
+		return(errno * -1);
 	}
 
 	fi->fh = fh;
 
 	return(0);
@@ -1298,48 +1356,90 @@
 
 	appfs_get_path_info_cache_rm(path, appfs_get_fsuid());
 
 	close_ret = close(fi->fh);
 	if (close_ret != 0) {
-		return(-EIO);
+		APPFS_DEBUG("error: close failed");
+
+		return(errno * -1);
 	}
 
 	return(0);
 }
 
 static int appfs_fuse_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
-	off_t lseek_ret;
 	ssize_t read_ret;
+	int retval;
 
-	APPFS_DEBUG("Enter (path = %s, ...)", path);
+	APPFS_DEBUG("Enter (path = %s, buf, %lli, %lli, fd=%lli)", path, (long long) size, (long long) offset, (long long) fi->fh);
 
-	lseek_ret = lseek(fi->fh, offset, SEEK_SET);
-	if (lseek_ret != offset) {
-		return(-EIO);
+	retval = 0;
+
+	while (size != 0) {
+		read_ret = pread(fi->fh, buf, size, offset);
+
+		if (read_ret < 0) {
+			APPFS_DEBUG("error: read failed");
+
+			return(errno * -1);
+		}
+
+		if (read_ret == 0) {
+			break;
+		}
+
+		size -= read_ret;
+		buf  += read_ret;
+		offset += read_ret;
+		retval += read_ret;
+	}
+
+	if (size != 0) {
+		APPFS_DEBUG("error: incomplete read (this is an error because FUSE will request the exact length of the file)");
+
+		return(0);
 	}
 
-	read_ret = read(fi->fh, buf, size);
+	APPFS_DEBUG("Returning: %i", retval);
 
-	return(read_ret);
+	return(retval);
 }
 
 static int appfs_fuse_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
-	off_t lseek_ret;
 	ssize_t write_ret;
+	int retval;
 
 	APPFS_DEBUG("Enter (path = %s, ...)", path);
 
 	appfs_get_path_info_cache_rm(path, appfs_get_fsuid());
 
-	lseek_ret = lseek(fi->fh, offset, SEEK_SET);
-	if (lseek_ret != offset) {
-		return(-EIO);
+	retval = 0;
+
+	while (size != 0) {
+		write_ret = pwrite(fi->fh, buf, size, offset);
+
+		if (write_ret < 0) {
+			APPFS_DEBUG("error: write failed");
+
+			return(errno * -1);
+		}
+
+		if (write_ret == 0) {
+			break;
+		}
+
+		size -= write_ret;
+		buf  += write_ret;
+		offset += write_ret;
+		retval += write_ret;
+	}
+
+	if (size != 0) {
+		APPFS_DEBUG("error: incomplete write");
 	}
 
-	write_ret = write(fi->fh, buf, size);
-
-	return(write_ret);
+	return(retval);
 }
 
 static int appfs_fuse_mknod(const char *path, mode_t mode, dev_t device) {
 	char *real_path;
 	int mknod_ret;
@@ -1766,11 +1866,11 @@
 }
 
 /*
  * Terminate a thread
  */
-static void appfs_terminate_interp(void *_interp) {
+static void appfs_terminate_interp_and_thread(void *_interp) {
 	Tcl_Interp *interp;
 
 	APPFS_DEBUG("Called: _interp = %p", _interp);
 
 	if (_interp == NULL) {
@@ -1784,10 +1884,12 @@
 	APPFS_DEBUG("Terminating interpreter due to thread termination");
 
 	appfs_call_libtcl(
 		Tcl_DeleteInterp(interp);
 	)
+
+	Tcl_FinalizeThread();
 
 	return;
 }
 
 /*
@@ -1868,11 +1970,11 @@
 	/*
 	 * Create a thread-specific-data (TSD) key for each thread to refer
 	 * to its own Tcl interpreter.  Tcl interpreters must be unique per
 	 * thread and new threads are dynamically created by FUSE.
 	 */
-	pthread_ret = pthread_key_create(&interpKey, appfs_terminate_interp);
+	pthread_ret = pthread_key_create(&interpKey, appfs_terminate_interp_and_thread);
 	if (pthread_ret != 0) {
 		fprintf(stderr, "Unable to create TSD key for Tcl.  Aborting.\n");
 
 		return(1);
 	}