Index: appfsd.c ================================================================== --- appfsd.c +++ appfsd.c @@ -255,83 +255,10 @@ } 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); - - interp = appfs_TclInterp(); - if (interp == NULL) { - return; - } - - tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getindex", hostname); - if (tcl_ret != TCL_OK) { - APPFS_DEBUG("Call to ::appfs::getindex failed: %s", Tcl_GetStringResult(interp)); - - return; - } - - 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; - - interp = appfs_TclInterp(); - if (interp == NULL) { - return(NULL); - } - - tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::download", hostname, sha1); - if (tcl_ret != TCL_OK) { - APPFS_DEBUG("Call to ::appfs::download failed: %s", Tcl_GetStringResult(interp)); - - return(NULL); - } - - 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(); - if (interp == NULL) { - return; - } - - tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::getpkgmanifest", hostname, sha1); - if (tcl_ret != TCL_OK) { - APPFS_DEBUG("Call to ::appfs::getpkgmanifest failed: %s", Tcl_GetStringResult(interp)); - - return; - } - - 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. */ @@ -483,16 +410,12 @@ struct appfs_pathinfo pathinfo; int res = 0; APPFS_DEBUG("Enter (path = %s, ...)", path); - pathinfo.type = APPFS_PATHTYPE_INVALID; - - res = appfs_get_path_info(path, &pathinfo, NULL); - if (res != 0) { - return(res); - } + pathinfo.type = APPFS_PATHTYPE_DIRECTORY; + pathinfo.typeinfo.dir.childcount = 0; memset(stbuf, 0, sizeof(struct stat)); stbuf->st_mtime = pathinfo.time; stbuf->st_ctime = pathinfo.time; @@ -534,29 +457,46 @@ return res; } static int appfs_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { - struct appfs_pathinfo pathinfo; - struct appfs_children *children, *child; + Tcl_Interp *interp; + Tcl_Obj **children; + int children_count, idx; + int tcl_ret; int retval; APPFS_DEBUG("Enter (path = %s, ...)", path); - retval = appfs_get_path_info(path, &pathinfo, &children); - if (retval != 0) { - return(retval); + interp = appfs_TclInterp(); + if (interp == NULL) { + return(0); } filler(buf, ".", NULL, 0); filler(buf, "..", NULL, 0); - for (child = children; child; child = child->_next) { - filler(buf, child->name, NULL, 0); + + tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getchildren", path); + if (tcl_ret != TCL_OK) { + APPFS_DEBUG("::appfs::getchildren(%s) failed.", path); + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); + + return(0); + } + + tcl_ret = Tcl_ListObjGetElements(interp, Tcl_GetObjResult(interp), &children_count, &children); + if (tcl_ret != TCL_OK) { + APPFS_DEBUG("Parsing list of children on path %s failed.", path); + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); + + return(0); } -// appfs_free_list_children(children); + for (idx = 0; idx < children_count; idx++) { + filler(buf, Tcl_GetString(children[idx]), NULL, 0); + } return(0); } static int appfs_fuse_open(const char *path, struct fuse_file_info *fi) { Index: appfsd.tcl ================================================================== --- appfsd.tcl +++ appfsd.tcl @@ -6,10 +6,15 @@ namespace eval ::appfs { variable cachedir "/tmp/appfs-cache" variable ttl 3600 variable nttl 60 + + # User-replacable function to convert a hostname/hash/method to an URL + proc _construct_url {hostname hash method} { + return "http://$hostname/appfs/$method/$hash" + } proc _hash_sep {hash {seps 4}} { for {set idx 0} {$idx < $seps} {incr idx} { append retval "[string range $hash [expr {$idx * 2}] [expr {($idx * 2) + 1}]]/" } @@ -125,10 +130,16 @@ catch { parray does_not_exist } set ::appfs::init_called 1 + + # Load configuration file + set config_file [file join $::appfs::cachedir config] + if {[file exists $config_file]} { + source $config_file + } if {![info exists ::appfs::db]} { file mkdir $::appfs::cachedir sqlite3 ::appfs::db [file join $::appfs::cachedir cache.db] @@ -144,11 +155,11 @@ db eval {CREATE INDEX IF NOT EXISTS packages_index ON packages (hostname, package, version, os, cpuArch);} db eval {CREATE INDEX IF NOT EXISTS files_index ON files (package_sha1, file_name, file_directory);} } proc download {hostname hash {method sha1}} { - set url "http://$hostname/appfs/$method/$hash" + set url [_construct_url $hostname $hash $method] set file [_cachefile $url $hash] if {![file exists $file]} { return -code error "Unable to fetch (file does not exist: $file)" } @@ -326,6 +337,105 @@ } } return COMPLETE } + + proc _parsepath {path} { + set path [string trim $path "/"] + set path [split $path "/"] + set pathlen [llength $path] + + array set retval [list _children sites] + + if {$pathlen > 0} { + set retval(hostname) [lindex $path 0] + set retval(_children) packages + + if {$pathlen > 1} { + set package [lindex $path 1] + if {[string length $package] == "40" && [regexp {^[a-fA-F0-9]*$} $package]} { + set retval(package_sha1) $package + set retval(_children) files + + if {$pathlen > 2} { + set retval(file) [join [lrange $path 2 end] "/"] + } else { + set retval(file) "" + } + + return [array get retval] + } else { + set retval(package) $package + set retval(_children) os-cpu + } + + if {$pathlen > 2} { + set os_cpu [lindex $path 2] + set os_cpu [split $os_cpu "-"] + + set retval(os) [lindex $os_cpu 0] + set retval(cpu) [lindex $os_cpu 1] + set retval(_children) versions + + if {$pathlen > 3} { + set retval(version) [lindex $path 3] + set retval(_children) files + + set retval(package_sha1) [::appfs::db onecolumn {SELECT sha1 FROM packages WHERE hostname = $retval(hostname) AND os = $retval(os) AND cpuArch = $retval(cpu) AND version = $retval(version);}] + if {$retval(package_sha1) == ""} { + return [list] + } + + if {$pathlen > 4} { + set retval(file) [join [lrange $path 4 end] "/"] + } else { + set retval(file) "" + } + } + } + } + } + + return [array get retval] + } + + proc getchildren {dir} { + array set pathinfo [_parsepath $dir] + + switch -- $pathinfo(_children) { + "sites" { + return [::appfs::db eval {SELECT DISTINCT hostname FROM packages;}] + } + "packages" { + catch { + ::appfs::getindex $pathinfo(hostname) + } + + return [::appfs::db eval {SELECT DISTINCT package FROM packages WHERE hostname = $pathinfo(hostname);}] + } + "os-cpu" { + return [::appfs::db eval {SELECT DISTINCT os || "-" || cpuArch FROM packages WHERE hostname = $pathinfo(hostname) AND package = $pathinfo(package);}] + } + "versions" { + return [::appfs::db eval { + SELECT DISTINCT version FROM packages WHERE hostname = $pathinfo(hostname) AND package = $pathinfo(package) AND os = $pathinfo(os) AND cpuArch = $pathinfo(cpu); + }] + } + "files" { + catch { + ::appfs::getpkgmanifest $pathinfo(hostname) $pathinfo(package_sha1) + } + + return [::appfs::db eval {SELECT DISTINCT file_name FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_directory = $pathinfo(file);}] + } + } + + return -code error "Invalid or unacceptable path: $dir" + } + + proc getattr {path} { + } + + proc openpath {path mode} { + } }