Index: appfsd.c
==================================================================
--- appfsd.c
+++ appfsd.c
@@ -44,15 +44,23 @@
  */
 const char *appfs_cachedir;
 time_t appfs_boottime;
 int appfs_fuse_started = 0;
 
+/*
+ * Global variables for AppFS caching
+ */
+pthread_mutex_t appfs_path_info_cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+int appfs_path_info_cache_size = 8209;
+struct appfs_pathinfo *appfs_path_info_cache = NULL;
+
 /*
  * AppFS Path Type:  Describes the type of path a given file is
  */
 typedef enum {
 	APPFS_PATHTYPE_INVALID,
+	APPFS_PATHTYPE_DOES_NOT_EXIST,
 	APPFS_PATHTYPE_FILE,
 	APPFS_PATHTYPE_DIRECTORY,
 	APPFS_PATHTYPE_SYMLINK,
 	APPFS_PATHTYPE_SOCKET,
 	APPFS_PATHTYPE_FIFO,
@@ -80,10 +88,14 @@
 		struct {
 			off_t size;
 			char source[256];
 		} symlink;
 	} typeinfo;
+
+	/* Attributes used only for caching entries */
+	char *_cache_path;
+	uid_t _cache_uid;
 };
 
 /*
  * Create a new Tcl interpreter and completely initialize it
  */
@@ -405,88 +417,10 @@
 	retval = strdup(result->pw_dir);
 
 	return(retval);
 }
 
-/*
- * Tcl interface to get the home directory for the user making the "current"
- * FUSE I/O request
- */
-static int tcl_appfs_get_homedir(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
-	char *homedir;
-	Tcl_Obj *homedir_obj;
-	uid_t fsuid;
-	static __thread Tcl_Obj *last_homedir_obj = NULL;
-	static __thread uid_t last_fsuid = -1;
-
-        if (objc != 1) {
-                Tcl_WrongNumArgs(interp, 1, objv, NULL);
-                return(TCL_ERROR);
-        }
-
-	fsuid = appfs_get_fsuid();
-
-	if (fsuid == last_fsuid && last_homedir_obj != NULL) {
-		homedir_obj = last_homedir_obj;
-	} else {
-		homedir = appfs_get_homedir(appfs_get_fsuid());
-
-		if (homedir == NULL) {
-			return(TCL_ERROR);
-		}
-
-		homedir_obj = Tcl_NewStringObj(homedir, -1);
-
-		free(homedir);
-
-		if (last_homedir_obj != NULL) {
-			Tcl_DecrRefCount(last_homedir_obj);
-		}
-
-		last_homedir_obj = homedir_obj;
-		last_fsuid = fsuid;
-
-		Tcl_IncrRefCount(last_homedir_obj);
-	}
-
-       	Tcl_SetObjResult(interp, homedir_obj);
-
-        return(TCL_OK);
-}
-
-static int tcl_appfs_simulate_user_fs_enter(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
-	appfs_simulate_user_fs_enter();
-
-	return(TCL_OK);
-}
-
-static int tcl_appfs_simulate_user_fs_leave(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
-	appfs_simulate_user_fs_leave();
-
-	return(TCL_OK);
-}
-
-static int tcl_appfs_get_fsuid(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
-	uid_t fsuid;
-
-	fsuid = appfs_get_fsuid();
-
-       	Tcl_SetObjResult(interp, Tcl_NewWideIntObj(fsuid));
-
-	return(TCL_OK);
-}
-
-static int tcl_appfs_get_fsgid(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
-	gid_t fsgid;
-
-	fsgid = appfs_get_fsgid();
-
-       	Tcl_SetObjResult(interp, Tcl_NewWideIntObj(fsgid));
-
-	return(TCL_OK);
-}
-
 /*
  * Generate an inode for a given path.  The inode should be computed in such
  * a way that it is unlikely to be duplicated and remains the same for a given
  * file
  */
@@ -505,20 +439,193 @@
 	retval += 10;
 	retval %= 4294967296ULL;
 
 	return(retval);
 }
+
+/*
+ * Cache Get Path Info lookups for speed
+ */
+static int appfs_get_path_info_cache_get(const char *path, uid_t uid, struct appfs_pathinfo *pathinfo) {
+	unsigned int hash_idx;
+	int pthread_ret;
+	int retval;
+
+	retval = 1;
+
+	pthread_ret = pthread_mutex_lock(&appfs_path_info_cache_mutex);
+	if (pthread_ret != 0) {
+		APPFS_DEBUG("Unable to lock path_info cache mutex !");
+
+		return(-1);
+	}
+
+	if (appfs_path_info_cache != NULL) {
+		hash_idx = (appfs_get_path_inode(path) + uid) % appfs_path_info_cache_size;
+
+		if (appfs_path_info_cache[hash_idx]._cache_path != NULL) {
+			if (strcmp(appfs_path_info_cache[hash_idx]._cache_path, path) == 0 && appfs_path_info_cache[hash_idx]._cache_uid == uid) {
+				retval = 0;
+
+				memcpy(pathinfo, &appfs_path_info_cache[hash_idx], sizeof(*pathinfo));
+				pathinfo->_cache_path = NULL;
+			}
+		}
+	}
+
+	pthread_ret = pthread_mutex_unlock(&appfs_path_info_cache_mutex);
+	if (pthread_ret != 0) {
+		APPFS_DEBUG("Unable to unlock path_info cache mutex !");
+
+		return(-1);
+	}
+
+	if (retval == 0) {
+		APPFS_DEBUG("Cache hit on %s", path);
+	} else {
+		APPFS_DEBUG("Cache miss on %s", path);
+	}
+
+	return(retval);
+}
+
+static void appfs_get_path_info_cache_add(const char *path, uid_t uid, struct appfs_pathinfo *pathinfo) {
+	unsigned int hash_idx;
+	int pthread_ret;
+
+	pthread_ret = pthread_mutex_lock(&appfs_path_info_cache_mutex);
+	if (pthread_ret != 0) {
+		APPFS_DEBUG("Unable to lock path_info cache mutex !");
+
+		return;
+	}
+
+	if (appfs_path_info_cache == NULL) {
+		appfs_path_info_cache = calloc(appfs_path_info_cache_size, sizeof(*appfs_path_info_cache));
+	}
+
+	hash_idx = (appfs_get_path_inode(path) + uid) % appfs_path_info_cache_size;
+
+	if (appfs_path_info_cache[hash_idx]._cache_path != NULL) {
+		free(appfs_path_info_cache[hash_idx]._cache_path);
+	}
+
+	memcpy(&appfs_path_info_cache[hash_idx], pathinfo, sizeof(*pathinfo));
+
+	appfs_path_info_cache[hash_idx]._cache_path = strdup(path);
+	appfs_path_info_cache[hash_idx]._cache_uid  = uid;
+
+	pthread_ret = pthread_mutex_unlock(&appfs_path_info_cache_mutex);
+	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;
+	int pthread_ret;
+
+	pthread_ret = pthread_mutex_lock(&appfs_path_info_cache_mutex);
+	if (pthread_ret != 0) {
+		APPFS_DEBUG("Unable to lock path_info cache mutex !");
+
+		return;
+	}
+
+	if (appfs_path_info_cache != NULL) {
+		hash_idx = (appfs_get_path_inode(path) + uid) % appfs_path_info_cache_size;
+
+		if (appfs_path_info_cache[hash_idx]._cache_path != NULL) {
+			free(appfs_path_info_cache[hash_idx]._cache_path);
+
+			appfs_path_info_cache[hash_idx]._cache_path = NULL;
+		}
+	}
+
+	pthread_ret = pthread_mutex_unlock(&appfs_path_info_cache_mutex);
+	if (pthread_ret != 0) {
+		APPFS_DEBUG("Unable to unlock path_info cache mutex !");
+
+		return;
+	}
+
+	return;
+}
+
+static void appfs_get_path_info_cache_flush(uid_t uid, int new_size) {
+	unsigned int idx;
+	int pthread_ret;
+
+	pthread_ret = pthread_mutex_lock(&appfs_path_info_cache_mutex);
+	if (pthread_ret != 0) {
+		APPFS_DEBUG("Unable to lock path_info cache mutex !");
+
+		return;
+	}
+
+	if (appfs_path_info_cache != NULL) {
+		for (idx = 0; idx < appfs_path_info_cache_size; idx++) {
+			if (appfs_path_info_cache[idx]._cache_path != NULL) {
+				if (uid != ((uid_t) -1)) {
+					if (appfs_path_info_cache[idx]._cache_uid != uid) {
+						continue;
+					}
+				}
+
+				free(appfs_path_info_cache[idx]._cache_path);
+
+				appfs_path_info_cache[idx]._cache_path = NULL;
+			}
+		}
+	}
+
+	if (uid == ((uid_t) -1)) {
+		free(appfs_path_info_cache);
+
+		appfs_path_info_cache = NULL;
+
+		if (new_size != -1) {
+			appfs_path_info_cache_size = new_size;
+		}
+	}
+
+	pthread_ret = pthread_mutex_unlock(&appfs_path_info_cache_mutex);
+	if (pthread_ret != 0) {
+		APPFS_DEBUG("Unable to unlock path_info cache mutex !");
+
+		return;
+	}
+
+	return;
+}
 
 /* Get information about a path, and optionally list children */
 static int appfs_get_path_info(const char *path, struct appfs_pathinfo *pathinfo) {
 	Tcl_Interp *interp;
 	Tcl_Obj *attrs_dict, *attr_value;
 	const char *attr_value_str;
 	Tcl_WideInt attr_value_wide;
 	int attr_value_int;
 	static __thread Tcl_Obj *attr_key_type = NULL, *attr_key_perms = NULL, *attr_key_size = NULL, *attr_key_time = NULL, *attr_key_source = NULL, *attr_key_childcount = NULL, *attr_key_packaged = NULL;
+	int cache_ret;
 	int tcl_ret;
+
+	cache_ret = appfs_get_path_info_cache_get(path, appfs_get_fsuid(), pathinfo);
+	if (cache_ret == 0) {
+		if (pathinfo->type == APPFS_PATHTYPE_DOES_NOT_EXIST) {
+			return(-ENOENT);
+		}
+
+		if (pathinfo->type == APPFS_PATHTYPE_INVALID) {
+			return(-EIO);
+		}
+
+		return(0);
+	}
 
 	interp = appfs_TclInterp();
 	if (interp == NULL) {
 		return(-EIO);
 	}
@@ -526,10 +633,14 @@
 	tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getattr", path);
 	if (tcl_ret != TCL_OK) {
 		APPFS_DEBUG("::appfs::getattr(%s) failed.", path);
 		APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp));
 
+		pathinfo->type = APPFS_PATHTYPE_DOES_NOT_EXIST;
+
+		appfs_get_path_info_cache_add(path, appfs_get_fsuid(), pathinfo);
+
 		return(-ENOENT);
 	}
 
 	if (attr_key_type == NULL) {
 		attr_key_type       = Tcl_NewStringObj("type", -1);
@@ -633,17 +744,21 @@
 		}
 	} else {
 		pathinfo->time = 0;
 	}
 
+	appfs_get_path_info_cache_add(path, appfs_get_fsuid(), pathinfo);
+
 	return(0);
 }
 
 static char *appfs_prepare_to_create(const char *path) {
 	Tcl_Interp *interp;
 	const char *real_path;
 	int tcl_ret;
+
+	appfs_get_path_info_cache_flush(appfs_get_fsuid(), -1);
 
 	interp = appfs_TclInterp();
 	if (interp == NULL) {
 		return(NULL);
 	}
@@ -767,13 +882,17 @@
 		case APPFS_PATHTYPE_FIFO:
 			stbuf->st_mode = S_IFIFO | 0555;
 			stbuf->st_nlink = 1;
 			stbuf->st_size = 0;
 			break;
-		case APPFS_PATHTYPE_INVALID:
+		case APPFS_PATHTYPE_DOES_NOT_EXIST:
 			retval = -ENOENT;
 
+			break;
+		case APPFS_PATHTYPE_INVALID:
+			retval = -EIO;
+
 			break;
 	}
 
 	if (pathinfo.packaged) {
 		stbuf->st_uid   = appfs_get_fsuid();
@@ -839,10 +958,16 @@
 		if (gpi_ret != 0 && gpi_ret != -ENOENT) {
 			return(gpi_ret);
 		}
 
 		mode = "create";
+
+		/*
+		 * We have to clear the cache here so that the number of
+		 * links gets maintained on the parent directory
+		 */
+		appfs_get_path_info_cache_flush(appfs_get_fsuid(), -1);
 	} else {
 		/* The file must already exist */
 		if (gpi_ret != 0) {
 			return(gpi_ret);
 		}
@@ -890,10 +1015,12 @@
 }
 
 static int appfs_fuse_close(const char *path, struct fuse_file_info *fi) {
 	int close_ret;
 
+	appfs_get_path_info_cache_rm(path, appfs_get_fsuid());
+
 	close_ret = close(fi->fh);
 	if (close_ret != 0) {
 		return(-EIO);
 	}
 
@@ -919,10 +1046,12 @@
 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;
 
 	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);
 	}
@@ -1011,10 +1140,12 @@
 	real_path = appfs_localpath(path);
 	if (real_path == NULL) {
 		return(-EIO);
 	}
 
+	appfs_get_path_info_cache_rm(path, appfs_get_fsuid());
+
 	appfs_simulate_user_fs_enter();
 
 	truncate_ret = truncate(real_path, size);
 
 	appfs_simulate_user_fs_leave();
@@ -1031,10 +1162,12 @@
 static int appfs_fuse_unlink_rmdir(const char *path) {
 	Tcl_Interp *interp;
 	int tcl_ret;
 
 	APPFS_DEBUG("Enter (path = %s, ...)", path);
+
+	appfs_get_path_info_cache_flush(appfs_get_fsuid(), -1);
 
 	interp = appfs_TclInterp();
 	if (interp == NULL) {
 		return(-EIO);
 	}
@@ -1053,11 +1186,10 @@
 static int appfs_fuse_mkdir(const char *path, mode_t mode) {
 	char *real_path;
 	int mkdir_ret;
 
 	APPFS_DEBUG("Enter (path = %s, ...)", path);
-
 
 	real_path = appfs_prepare_to_create(path);
 	if (real_path == NULL) {
 		return(-EIO);
 	}
@@ -1083,10 +1215,12 @@
 	Tcl_Interp *interp;
 	const char *real_path;
 	int tcl_ret, chmod_ret;
 
 	APPFS_DEBUG("Enter (path = %s, ...)", path);
+
+	appfs_get_path_info_cache_rm(path, appfs_get_fsuid());
 
 	interp = appfs_TclInterp();
 	if (interp == NULL) {
 		return(-EIO);
 	}
@@ -1178,10 +1312,109 @@
 /*
  * AppFSd Package for Tcl:
  *         Bridge for I/O operations to request information about the current
  *         transaction
  */
+/*
+ * Tcl interface to get the home directory for the user making the "current"
+ * FUSE I/O request
+ */
+static int tcl_appfs_get_homedir(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
+	char *homedir;
+	Tcl_Obj *homedir_obj;
+	uid_t fsuid;
+	static __thread Tcl_Obj *last_homedir_obj = NULL;
+	static __thread uid_t last_fsuid = -1;
+
+        if (objc != 1) {
+                Tcl_WrongNumArgs(interp, 1, objv, NULL);
+                return(TCL_ERROR);
+        }
+
+	fsuid = appfs_get_fsuid();
+
+	if (fsuid == last_fsuid && last_homedir_obj != NULL) {
+		homedir_obj = last_homedir_obj;
+	} else {
+		homedir = appfs_get_homedir(appfs_get_fsuid());
+
+		if (homedir == NULL) {
+			return(TCL_ERROR);
+		}
+
+		homedir_obj = Tcl_NewStringObj(homedir, -1);
+
+		free(homedir);
+
+		if (last_homedir_obj != NULL) {
+			Tcl_DecrRefCount(last_homedir_obj);
+		}
+
+		last_homedir_obj = homedir_obj;
+		last_fsuid = fsuid;
+
+		Tcl_IncrRefCount(last_homedir_obj);
+	}
+
+       	Tcl_SetObjResult(interp, homedir_obj);
+
+        return(TCL_OK);
+}
+
+static int tcl_appfs_simulate_user_fs_enter(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
+	appfs_simulate_user_fs_enter();
+
+	return(TCL_OK);
+}
+
+static int tcl_appfs_simulate_user_fs_leave(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
+	appfs_simulate_user_fs_leave();
+
+	return(TCL_OK);
+}
+
+static int tcl_appfs_get_fsuid(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
+	uid_t fsuid;
+
+	fsuid = appfs_get_fsuid();
+
+       	Tcl_SetObjResult(interp, Tcl_NewWideIntObj(fsuid));
+
+	return(TCL_OK);
+}
+
+static int tcl_appfs_get_fsgid(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
+	gid_t fsgid;
+
+	fsgid = appfs_get_fsgid();
+
+       	Tcl_SetObjResult(interp, Tcl_NewWideIntObj(fsgid));
+
+	return(TCL_OK);
+}
+
+static int tcl_appfs_get_path_info_cache_flush(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
+	int tcl_ret;
+	int new_size;
+
+	new_size = -1;
+
+	if (objc == 2) {
+		tcl_ret = Tcl_GetIntFromObj(interp, objv[1], &new_size);
+		if (tcl_ret != TCL_OK) {
+			return(tcl_ret);
+		}
+	} else if (objc > 2 || objc < 1) {
+                Tcl_WrongNumArgs(interp, 1, objv, "?new_cache_size?");
+		return(TCL_ERROR);
+	}
+
+	appfs_get_path_info_cache_flush(-1, new_size);
+
+	return(TCL_OK);
+}
+
 static int Appfsd_Init(Tcl_Interp *interp) {
 #ifdef USE_TCL_STUBS
 	if (Tcl_InitStubs(interp, TCL_VERSION, 0) == 0L) {
 		return(TCL_ERROR);
 	}
@@ -1190,10 +1423,11 @@
 	Tcl_CreateObjCommand(interp, "appfsd::get_homedir", tcl_appfs_get_homedir, NULL, NULL);
 	Tcl_CreateObjCommand(interp, "appfsd::get_fsuid", tcl_appfs_get_fsuid, NULL, NULL);
 	Tcl_CreateObjCommand(interp, "appfsd::get_fsgid", tcl_appfs_get_fsgid, NULL, NULL);
 	Tcl_CreateObjCommand(interp, "appfsd::simulate_user_fs_enter", tcl_appfs_simulate_user_fs_enter, NULL, NULL);
 	Tcl_CreateObjCommand(interp, "appfsd::simulate_user_fs_leave", tcl_appfs_simulate_user_fs_leave, NULL, NULL);
+	Tcl_CreateObjCommand(interp, "appfsd::get_path_info_cache_flush", tcl_appfs_get_path_info_cache_flush, NULL, NULL);
 
 	Tcl_PkgProvide(interp, "appfsd", "1.0");
 
 	return(TCL_OK);
 }

Index: appfsd.tcl
==================================================================
--- appfsd.tcl
+++ appfsd.tcl
@@ -280,10 +280,11 @@
 			if {$pkgInfo(isLatest)} {
 				db eval {UPDATE packages SET isLatest = 0 WHERE hostname = $hostname AND package = $pkgInfo($package) AND os = $pkgInfo($package) AND cpuArch = $pkgInfo(cpuArch);}
 			}
 
 			db eval {INSERT INTO packages (hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest) VALUES ($hostname, $pkgInfo(hash), $pkgInfo(package), $pkgInfo(version), $pkgInfo(os), $pkgInfo(cpuArch), $pkgInfo(isLatest), 0);}
+
 		}
 
 		# Look for packages that have been deleted
 		set found_packages [db eval {SELECT sha1 FROM packages WHERE hostname = $hostname;}]
 		foreach package $found_packages {
@@ -297,10 +298,12 @@
 		foreach package [array names found_packages_arr] {
 			db eval {DELETE FROM packages WHERE hostname = $hostname AND sha1 = $package;}
 		}
 
 		db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::ttl);}
+
+		appfsd::get_path_info_cache_flush
 
 		return COMPLETE
 	}
 
 	proc getpkgmanifest {hostname package_sha1} {
@@ -356,10 +359,12 @@
 
 				db eval {INSERT INTO files (package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory) VALUES ($package_sha1, $fileInfo(type), $fileInfo(time), $fileInfo(source), $fileInfo(size), $fileInfo(perms), $fileInfo(sha1), $fileInfo(name), $fileInfo(directory) );}
 				db eval {UPDATE packages SET haveManifest = 1 WHERE sha1 = $package_sha1;}
 			}
 		}
+
+		appfsd::get_path_info_cache_flush
 
 		return COMPLETE
 	}
 
 	proc _localpath {package hostname file} {