Index: README.md ================================================================== --- README.md +++ README.md @@ -16,28 +16,29 @@ /opt/appfs/hostname Fetches: http://hostname/appfs/index Contains CSV file: hash,hashMethod,, \-------------/ ^- Signed data - Fetches: http://hostname/appfs/sha1/ - Contains CSV file: package,version,os,cpuArch,sha1,isLatest + Fetches: http://hostname/appfs// + Contains CSV file: package,version,os,cpuArch,packageManifestHash,isLatest /opt/appfs/hostname/package/os-cpuArch/version - /opt/appfs/hostname/sha1/ - Fetches: http://hostname/appfs/sha1/ + /opt/appfs/hostname// + Fetches: http://hostname/appfs// Contains CSV file: type,time,extraData,name type == directory; extraData = (null) type == symlink; extraData = source - type == file; extraData = size,perms,sha1 + type == file; extraData = size,perms,fileHash - /opt/appfs/hostname/{sha1,package/os-cpuArch/version}/file - Fetches: http://hostname/appfs/sha1/ + /opt/appfs/hostname/{packageManifestHash,package/os-cpuArch/version}/file + Fetches: http://hostname/appfs// Database -------- - packages(hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest) - files(package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory) + sites(hostname, hashMethod, lastUpdate, ttl) + packages(hostname, packageManifestHash, package, version, os, cpuArch, isLatest, haveManifest) + files(packageManifestHash, type, time, source, size, perms, fileHash, file_name, file_directory) Resources --------- http://appfs.rkeene.org/ Index: appfsd.tcl ================================================================== --- appfsd.tcl +++ appfsd.tcl @@ -42,17 +42,19 @@ proc get_homedir {} { return [::appfsd::get_homedir] } # User-replacable function to update permissions - proc change_perms {file sha1 perms} { + proc change_perms {file hash perms {hashMethod "sha1"}} { if {[info exists ::appfs::user::add_perms($file)]} { append perms $::appfs::user::add_perms($file) } - if {[info exists ::appfs::user::add_perms($sha1)]} { - append perms $::appfs::user::add_perms($sha1) + if {[info exists ::appfs::user::add_perms([list $hashMethod $hash])]} { + append perms $::appfs::user::add_perms([list $hashMethod $hash]) + } elseif {$hashMethod eq "sha1" && [info exists ::appfs::user::add_perms($hash)]} { + append perms $::appfs::user::add_perms($hash) } return $perms } @@ -166,31 +168,27 @@ proc _isHash {value} { set value [string tolower $value] - if {[string length $value] != 40} { - return false - } - if {![regexp {^[0-9a-f]*$} $value]} { return false } return true } - proc _verifySignatureAndCertificate {hostname certificate signature hash} { + proc _verifySignatureAndCertificate {hostname certificate signature hash hashmethod} { set certificate [binary format "H*" $certificate] set signature [binary format "H*" $signature] set certificate [::pki::x509::parse_cert $certificate] array set certificate_arr $certificate set certificate_cn [::pki::x509::_dn_to_cn $certificate_arr(subject)] - if {![::pki::verify $signature "$hash,sha1" $certificate]} { + if {![::pki::verify $signature "$hash,$hashmethod" $certificate]} { return false } if {[string tolower $certificate_cn] != [string tolower $hostname]} { return false @@ -310,18 +308,18 @@ ::appfs::db timeout 30000 } # Create tables - db eval {CREATE TABLE IF NOT EXISTS sites(hostname PRIMARY KEY, lastUpdate, ttl);} - db eval {CREATE TABLE IF NOT EXISTS packages(hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest);} - db eval {CREATE TABLE IF NOT EXISTS files(package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory);} + db eval {CREATE TABLE IF NOT EXISTS sites(hostname PRIMARY KEY, hashMethod, lastUpdate, ttl);} + db eval {CREATE TABLE IF NOT EXISTS packages(hostname, packageManifestHash, package, version, os, cpuArch, isLatest, haveManifest);} + db eval {CREATE TABLE IF NOT EXISTS files(packageManifestHash, hashMethod, type, time, source, size, perms, fileHash, file_name, file_directory);} # Create indexes db eval {CREATE INDEX IF NOT EXISTS sites_index ON sites (hostname);} - db eval {CREATE INDEX IF NOT EXISTS packages_index ON packages (hostname, sha1, package, version, os, cpuArch);} - db eval {CREATE INDEX IF NOT EXISTS files_index ON files (package_sha1, file_name, file_directory);} + db eval {CREATE INDEX IF NOT EXISTS packages_index ON packages (hostname, packageManifestHash, package, version, os, cpuArch);} + db eval {CREATE INDEX IF NOT EXISTS files_index ON files (packageManifestHash, file_name, file_directory);} } proc download {hostname hash {method sha1}} { set url [::appfs::user::construct_url $hostname $hash $method] set file [_cachefile $url $hash $method] @@ -376,15 +374,15 @@ if {![_isHash $indexhash]} { return -code error "Invalid hash: $indexhash" } - if {![_verifySignatureAndCertificate $hostname $indexhashcert $indexhashsig $indexhash]} { + if {![_verifySignatureAndCertificate $hostname $indexhashcert $indexhashsig $indexhash $indexhashmethod]} { return -code error "Invalid signature or certificate from $hostname" } - set file [download $hostname $indexhash] + set file [download $hostname $indexhash $indexhashmethod] catch { set fd [open $file] } if {![info exists fd]} { @@ -417,11 +415,11 @@ set pkgInfo(package) [lindex $work 0] set pkgInfo(version) [lindex $work 1] set pkgInfo(os) [_normalizeOS [lindex $work 2]] set pkgInfo(cpuArch) [_normalizeCPU [lindex $work 3]] set pkgInfo(hash) [string tolower [lindex $work 4]] - set pkgInfo(hash_type) "sha1" + set pkgInfo(hash_type) $indexhashmethod set pkgInfo(isLatest) [expr {!![lindex $work 5]}] }]} { continue } @@ -430,20 +428,20 @@ } lappend curr_packages $pkgInfo(hash) # Do not do any additional work if we already have this package - set existing_packages [db eval {SELECT package FROM packages WHERE hostname = $hostname AND sha1 = $pkgInfo(hash);}] + set existing_packages [db eval {SELECT package FROM packages WHERE hostname = $hostname AND packageManifestHash = $pkgInfo(hash);}] if {[lsearch -exact $existing_packages $pkgInfo(package)] != -1} { continue } if {$pkgInfo(isLatest)} { db eval {UPDATE packages SET isLatest = 0 WHERE hostname = $hostname AND package = $pkgInfo(package) AND os = $pkgInfo(os) 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);} + db eval {INSERT INTO packages (hostname, packageManifestHash, 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 { @@ -453,32 +451,33 @@ foreach package $curr_packages { unset -nocomplain found_packages_arr($package) } foreach package [array names found_packages_arr] { - db eval {DELETE FROM packages WHERE hostname = $hostname AND sha1 = $package;} + db eval {DELETE FROM packages WHERE hostname = $hostname AND packageManifestHash = $package;} } - db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::ttl);} + db eval {INSERT OR REPLACE INTO sites (hostname, hashMethod, lastUpdate, ttl) VALUES ($hostname, $indexhashmethod, $now, $::appfs::ttl);} appfsd::get_path_info_cache_flush return COMPLETE } - proc getpkgmanifest {hostname package_sha1} { - set haveManifest [db onecolumn {SELECT haveManifest FROM packages WHERE sha1 = $package_sha1 LIMIT 1;}] + proc getpkgmanifest {hostname packageManifestHash} { + set haveManifest [db onecolumn {SELECT haveManifest FROM packages WHERE packageManifestHash = $packageManifestHash LIMIT 1;}] + set siteHashMethod [db onecolumn {SELECT hashMethod FROM sites WHERE hostname = $hostname LIMIT 1;} if {$haveManifest == "1"} { return COMPLETE } - if {![_isHash $package_sha1]} { + if {![_isHash $packageManifestHash]} { return FAIL } - set file [download $hostname $package_sha1] + set file [download $hostname $packageManifestHash $siteHashMethod] catch { set fd [open $file] }