@@ -11,49 +11,71 @@ #include #include #include #include -/* From sha1.c */ -int Sha1_Init(Tcl_Interp *interp); - +/* + * Default cache directory + */ #ifndef APPFS_CACHEDIR #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"); } #else #define APPFS_DEBUG(x...) /**/ #endif +/* + * SHA1 Tcl Package initializer, from sha1.o + */ +int Sha1_Init(Tcl_Interp *interp); + +/* + * Thread Specific Data (TSD) for Tcl Interpreter for the current thread + */ static pthread_key_t interpKey; +/* + * Data for a given thread (most likely these will become just globable variables soon.) + */ struct appfs_thread_data { const char *cachedir; time_t boottime; struct { int writable; } options; }; +/* The global thread data (likely to go away) */ struct appfs_thread_data globalThread; +/* + * AppFS Path Type: Describes the type of path a given file is + */ typedef enum { APPFS_PATHTYPE_INVALID, APPFS_PATHTYPE_FILE, APPFS_PATHTYPE_DIRECTORY, APPFS_PATHTYPE_SYMLINK } appfs_pathtype_t; +/* + * AppFS Children Files linked-list + */ struct appfs_children { struct appfs_children *_next; - int counter; - char name[256]; }; +/* + * AppFS Path Information: + * Completely describes a specific path, how it should be returned to + * to the kernel + */ struct appfs_pathinfo { appfs_pathtype_t type; time_t time; char hostname[256]; int packaged; @@ -71,10 +93,13 @@ char source[256]; } symlink; } typeinfo; }; +/* + * Create a new Tcl interpreter and completely initialize it + */ static Tcl_Interp *appfs_create_TclInterp(void) { Tcl_Interp *interp; const char *cachedir = globalThread.cachedir; int tcl_ret; @@ -115,10 +140,15 @@ Tcl_DeleteInterp(interp); return(NULL); } + /* + * Load the "appfsd.tcl" script, which is "compiled" into a C header + * so that it does not need to exist on the filesystem and can be + * directly evaluated. + */ tcl_ret = Tcl_Eval(interp, "" #include "appfsd.tcl.h" ""); if (tcl_ret != TCL_OK) { fprintf(stderr, "Unable to initialize Tcl AppFS script. Aborting.\n"); @@ -127,18 +157,25 @@ Tcl_DeleteInterp(interp); return(NULL); } + /* + * Set global variables from C to Tcl + */ if (Tcl_SetVar(interp, "::appfs::cachedir", cachedir, TCL_GLOBAL_ONLY) == NULL) { fprintf(stderr, "Unable to set cache directory. This should never fail.\n"); Tcl_DeleteInterp(interp); return(NULL); } + /* + * Initialize the "appfsd.tcl" environment, which must be done after + * global variables are set. + */ tcl_ret = Tcl_Eval(interp, "::appfs::init"); if (tcl_ret != TCL_OK) { fprintf(stderr, "Unable to initialize Tcl AppFS script (::appfs::init). Aborting.\n"); fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp)); @@ -145,33 +182,53 @@ Tcl_DeleteInterp(interp); return(NULL); } + /* + * Hide some Tcl commands that we do not care to use and which may + * slow down run-time operations. + */ Tcl_HideCommand(interp, "auto_load_index", "auto_load_index"); Tcl_HideCommand(interp, "unknown", "unknown"); + /* + * Return the completely initialized interpreter + */ return(interp); } +/* + * Return the thread-specific Tcl interpreter, creating it if needed + */ static Tcl_Interp *appfs_TclInterp(void) { Tcl_Interp *interp; + int pthread_ret; interp = pthread_getspecific(interpKey); if (interp == NULL) { interp = appfs_create_TclInterp(); if (interp == NULL) { return(NULL); } - pthread_setspecific(interpKey, interp); + pthread_ret = pthread_setspecific(interpKey, interp); + if (pthread_ret != 0) { + Tcl_DeleteInterp(interp); + + return(NULL); + } } return(interp); } +/* + * Evaluate a Tcl script constructed by concatenating a bunch of C strings + * together. + */ static int appfs_Tcl_Eval(Tcl_Interp *interp, int objc, const char *cmd, ...) { Tcl_Obj **objv; const char *arg; va_list argp; int retval; @@ -206,10 +263,13 @@ } return(retval); } +/* + * AppFS: Request that a host's package index be updated locally + */ static void appfs_update_index(const char *hostname) { Tcl_Interp *interp; int tcl_ret; APPFS_DEBUG("Enter: hostname = %s", hostname); @@ -227,10 +287,14 @@ } return; } +/* + * AppFS: Get a SHA1 from a host + * Returns a local file name, or NULL if it cannot be fetched + */ static const char *appfs_getfile(const char *hostname, const char *sha1) { Tcl_Interp *interp; char *retval; int tcl_ret; @@ -249,10 +313,14 @@ retval = strdup(Tcl_GetStringResult(interp)); return(retval); } +/* + * AppFS: Update the manifest for a specific package (by the package SHA1) on + * a given host + */ static void appfs_update_manifest(const char *hostname, const char *sha1) { Tcl_Interp *interp; int tcl_ret; interp = appfs_TclInterp(); @@ -268,10 +336,15 @@ } return; } +/* + * Determine the UID for the user making the current FUSE filesystem request. + * This will be used to lookup the user's home directory so we can search for + * locally modified files. + */ static uid_t appfs_get_fsuid(void) { struct fuse_context *ctx; ctx = fuse_get_context(); if (ctx == NULL) { @@ -281,10 +354,16 @@ } return(ctx->uid); } +/* + * Look up the home directory for a given UID + * Returns a C string containing the user's home directory or NULL if + * the user's home directory does not exist or is not correctly + * configured + */ static char *appfs_get_homedir(uid_t fsuid) { struct passwd entry, *result; struct stat stbuf; char buf[1024], *retval; int gpu_ret, stat_ret; @@ -328,10 +407,14 @@ 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; if (objc != 1) { Tcl_WrongNumArgs(interp, 1, objv, NULL); @@ -349,11 +432,15 @@ free(homedir); return(TCL_OK); } -/* Generate an inode for a given path */ +/* + * 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 + */ static long long appfs_get_path_inode(const char *path) { long long retval; const char *p; retval = 10; @@ -545,10 +632,13 @@ read_ret = read(fi->fh, buf, size); return(read_ret); } +/* + * SQLite3 mode: Execute raw SQL and return success or failure + */ static int appfs_sqlite3(const char *sql) { Tcl_Interp *interp; const char *sql_ret; int tcl_ret; @@ -573,10 +663,13 @@ } return(0); } +/* + * Tcl mode: Execute raw Tcl and return success or failure + */ static int appfs_tcl(const char *tcl) { Tcl_Interp *interp; const char *tcl_result; int tcl_ret; @@ -601,10 +694,15 @@ } return(0); } +/* + * AppFSd Package for Tcl: + * Bridge for I/O operations to request information about the current + * transaction + */ static int Appfsd_Init(Tcl_Interp *interp) { #ifdef USE_TCL_STUBS if (Tcl_InitStubs(interp, TCL_VERSION, 0) == 0L) { return(TCL_ERROR); } @@ -615,43 +713,78 @@ Tcl_PkgProvide(interp, "appfsd", "1.0"); return(TCL_OK); } +/* + * FUSE operations structure + */ static struct fuse_operations appfs_oper = { .getattr = appfs_fuse_getattr, .readdir = appfs_fuse_readdir, .readlink = appfs_fuse_readlink, .open = appfs_fuse_open, .release = appfs_fuse_close, .read = appfs_fuse_read }; +/* + * Entry point into this program. + */ int main(int argc, char **argv) { const char *cachedir = APPFS_CACHEDIR; int pthread_ret; + /* + * Set global variables, these should be configuration options. + */ globalThread.cachedir = cachedir; - globalThread.boottime = time(NULL); globalThread.options.writable = 1; + /* + * Set global variable for "boot time" to set a time on directories + * that we fake. + */ + globalThread.boottime = time(NULL); + + /* + * Register "sha1" and "appfsd" package with libtcl so that any new + * interpreters created (which are done dynamically by FUSE) can have + * the appropriate configuration done automatically. + */ Tcl_StaticPackage(NULL, "sha1", Sha1_Init, NULL); Tcl_StaticPackage(NULL, "appfsd", Appfsd_Init, NULL); + /* + * 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, NULL); if (pthread_ret != 0) { fprintf(stderr, "Unable to create TSD key for Tcl. Aborting.\n"); return(1); } + /* + * SQLite3 mode, for running raw SQL against the cache database + */ if (argc == 3 && strcmp(argv[1], "-sqlite3") == 0) { return(appfs_sqlite3(argv[2])); } + /* + * Tcl mode, for running raw Tcl in the same environment AppFSd would + * run code. + */ if (argc == 3 && strcmp(argv[1], "-tcl") == 0) { return(appfs_tcl(argv[2])); } + /* + * Enter the FUSE main loop -- this will process any arguments + * and start servicing requests. + */ return(fuse_main(argc, argv, &appfs_oper, NULL)); }