Changes In Branch tcl-ops Through [2160c4189b] Excluding Merge-Ins
This is equivalent to a diff from 98449bcf3e to 2160c4189b
2014-11-10
| ||
05:57 | Moved to tcl-ops branch check-in: 0effed3239 user: rkeene tags: trunk | |
03:34 | Updated to cache last home directory looked up and to setfsuid()/setfsgid() before accessing the filesystem so that we may be the right user check-in: 63e41c262c user: rkeene tags: tcl-ops | |
03:11 | Nearly completely working write support check-in: 2160c4189b user: rkeene tags: tcl-ops | |
2014-11-09
| ||
09:10 | Removed spurious deletes and fixed permissions on version directory check-in: 7d728e1078 user: rkeene tags: tcl-ops | |
2014-11-06
| ||
16:19 | Create new branch named "tcl-ops" check-in: a80b5fa283 user: rkeene tags: tcl-ops | |
03:05 | Updated to use package name instead of hash for looking up extra files check-in: 98449bcf3e user: rkeene tags: trunk | |
02:49 | Updated to indicate only that packaged files are writable check-in: 97e72202db user: rkeene tags: trunk | |
Modified .fossil-settings/ignore-glob from [5155de6731] to [6f815d9a84].
1 1 appfsd 2 2 appfsd.o 3 3 appfsd.tcl.h 4 +sha1.o 5 +sha1.tcl.h
Modified Makefile from [bee1193267] to [e0a242ebcc].
1 1 CC = gcc 2 2 PKG_CONFIG = pkg-config 3 3 FUSE_CFLAGS = $(shell $(PKG_CONFIG) --cflags fuse) 4 -SQLITE3_CFLAGS = $(shell $(PKG_CONFIG) --cflags sqlite3) 5 -CFLAGS = -Wall $(FUSE_CFLAGS) $(SQLITE3_CFLAGS) $(TCL_CFLAGS) -DDEBUG=1 4 +CFLAGS = -Wall $(FUSE_CFLAGS) $(TCL_CFLAGS) -DDEBUG=1 6 5 LDFLAGS = $(TCL_LDFLAGS) 7 6 FUSE_LIBS = $(shell $(PKG_CONFIG) --libs fuse) 8 -SQLITE3_LIBS = $(shell $(PKG_CONFIG) --libs sqlite3) 9 -LIBS = $(FUSE_LIBS) $(SQLITE3_LIBS) $(TCL_LIBS) 7 +LIBS = $(FUSE_LIBS) $(TCL_LIBS) 10 8 PREFIX = /usr/local 11 9 prefix = $(PREFIX) 12 10 bindir = $(prefix)/bin 13 11 sbindir = $(prefix)/sbin 14 12 15 13 ifneq ($(TCLKIT_SDK_DIR),) 16 14 TCLCONFIG_SH_PATH = $(TCLKIT_SDK_DIR)/lib/tclConfig.sh ................................................................................ 20 18 TCLCONFIG_SH_PATH = $(shell echo 'puts [::tcl::pkgconfig get libdir,install]' | tclsh)/tclConfig.sh 21 19 endif 22 20 TCL_CFLAGS = $(shell . $(TCLCONFIG_SH_PATH); echo "$${TCL_INCLUDE_SPEC}") 23 21 TCL_LIBS = $(shell . $(TCLCONFIG_SH_PATH); echo "$${TCL_LIB_SPEC}") 24 22 25 23 all: appfsd 26 24 27 -appfsd: appfsd.o 28 - $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o appfsd appfsd.o $(LIBS) 25 +appfsd: appfsd.o sha1.o 26 + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o appfsd appfsd.o sha1.o $(LIBS) 29 27 30 28 appfsd.o: appfsd.c appfsd.tcl.h 31 29 $(CC) $(CPPFLAGS) $(CFLAGS) -o appfsd.o -c appfsd.c 32 30 33 -appfsd.tcl.h: appfsd.tcl sha1.tcl 34 - sed '/@@SHA1\.TCL@@/ r sha1.tcl' appfsd.tcl | sed '/@@SHA1\.TCL@@/ d' | sed 's@[\\"]@\\&@g;s@^@ "@;s@$$@\\n"@' > appfsd.tcl.h.new 35 - mv appfsd.tcl.h.new appfsd.tcl.h 31 +sha1.o: sha1.c sha1.tcl.h 32 + $(CC) $(CPPFLAGS) $(CFLAGS) -o sha1.o -c sha1.c 33 + 34 +%.tcl.h: %.tcl 35 + sed 's@[\\"]@\\&@g;s@^@ "@;s@$$@\\n"@' $^ > $@.new 36 + mv $@.new $@ 36 37 37 -install: appfsd 38 +install: appfsd appfs-cache appfs-mkfs 38 39 if [ ! -d '$(DESTDIR)$(sbindir)' ]; then mkdir -p '$(DESTDIR)$(sbindir)'; chmod 755 '$(DESTDIR)$(sbindir)'; fi 40 + if [ ! -d '$(DESTDIR)$(bindir)' ]; then mkdir -p '$(DESTDIR)$(bindir)'; chmod 755 '$(DESTDIR)$(bindir)'; fi 39 41 cp appfsd '$(DESTDIR)$(sbindir)/' 42 + cp appfs-cache '$(DESTDIR)$(sbindir)/' 43 + cp appfs-mkfs '$(DESTDIR)$(bindir)/' 40 44 41 45 clean: 42 46 rm -f appfsd appfsd.o 43 47 rm -f appfsd.tcl.h 48 + rm -f sha1.o sha1.tcl.h 44 49 45 50 distclean: clean 46 51 47 52 .PHONY: all test clean distclean install
Added appfs-cache version [f56f48d37a].
1 +#! /usr/bin/env bash 2 + 3 +appfsd_options=() 4 +if [ "$1" == "--cachedir" ]; then 5 + appfsd_options=("${appfsd_options[@]}" '--cachedir' "$2") 6 + 7 + shift; shift; 8 +fi 9 + 10 +function call_appfsd() { 11 + appfsd "${appfsd_options[@]}" "$@" 12 +} 13 + 14 +function invalidate() { 15 + call_appfsd --sqlite3 'UPDATE sites SET ttl = "0";' 16 +} 17 + 18 +function remove_site() { 19 + local site 20 + 21 + site="$1" 22 + 23 + call_appfsd --sqlite3 'DELETE FROM sites WHERE hostname = '"'$site'"'; DELETE FROM packages WHERE hostname = '"'$site'"';' || return 1 24 + 25 + clean 26 +} 27 + 28 +function clean() { 29 + call_appfsd --tcl "$(cat <<\_EOF_ 30 + unset -nocomplain row 31 + ::appfs::db eval {SELECT sha1, hostname FROM packages;} row { 32 + set hostname [::appfs::db onecolumn {SELECT hostname FROM sites WHERE hostname = $row(hostname) LIMIT 1;}] 33 + if {$hostname == ""} { 34 + continue 35 + } 36 + 37 + set valid_sha1($row(sha1)) 1 38 + ::appfs::db eval {SELECT file_sha1 FROM files WHERE file_sha1 IS NOT NULL AND file_sha1 != '' AND package_sha1 = $row(sha1);} subrow { 39 + set valid_sha1($subrow(file_sha1)) 1 40 + } 41 + } 42 + 43 + foreach file [glob -nocomplain -tails -directory $::appfs::cachedir {[0-9a-f][0-9a-f]/*/*/*/*}] { 44 + set sha1 [string map [list "/" "" "\\" ""] $file] 45 + set file [file join $::appfs::cachedir $file] 46 + 47 + if {[info exists valid_sha1($sha1)]} { 48 + continue 49 + } 50 + 51 + puts "Cleaning $file" 52 + file delete -force -- $file 53 + } 54 +_EOF_ 55 + )" 56 +} 57 + 58 +function clear() { 59 + local package 60 + 61 + package="$1" 62 + 63 + if [ -n "${package}" ]; then 64 + echo "not implemented" >&2 65 + 66 + return 1 67 + fi 68 + 69 + call_appfsd --tcl 'file delete -force -- {*}[glob -directory $::appfs::cachedir {[0-9a-f][0-9a-f]}]' || return 1 70 + call_appfsd --sqlite3 'DELETE FROM sites; DELETE FROM packages; DELETE FROM files; VACUUM;' || return 1 71 +} 72 + 73 +case "$1" in 74 + invalidate) 75 + invalidate || exit 1 76 + ;; 77 + remove-site) 78 + remove_site "$2" || exit 1 79 + ;; 80 + clean) 81 + clean || exit 1 82 + ;; 83 + clear) 84 + clear "$2" || exit 1 85 + ;; 86 + *) 87 + echo "Usage: appfs-cache {invalidate|clean|clear|clear <package>|remove-site <site>}" >&2 88 + 89 + exit 1 90 + ;; 91 +esac 92 + 93 +exit 0
Modified appfsd.c from [251c8531b4] to [227a45ec34].
1 1 #define FUSE_USE_VERSION 26 2 2 3 3 #include <sys/types.h> 4 -#include <sqlite3.h> 5 4 #include <pthread.h> 6 5 #include <string.h> 7 6 #include <stdarg.h> 8 7 #include <stdlib.h> 9 8 #include <unistd.h> 10 9 #include <errno.h> 11 10 #include <fcntl.h> 12 11 #include <stdio.h> 13 12 #include <fuse.h> 14 13 #include <pwd.h> 15 14 #include <tcl.h> 16 15 16 +/* 17 + * Default cache directory 18 + */ 17 19 #ifndef APPFS_CACHEDIR 18 20 #define APPFS_CACHEDIR "/var/cache/appfs" 19 21 #endif 20 22 23 +/* Debugging macros */ 21 24 #ifdef DEBUG 22 25 #define APPFS_DEBUG(x...) { fprintf(stderr, "[debug] %s:%i:%s: ", __FILE__, __LINE__, __func__); fprintf(stderr, x); fprintf(stderr, "\n"); } 23 26 #else 24 27 #define APPFS_DEBUG(x...) /**/ 25 28 #endif 26 29 30 +/* 31 + * SHA1 Tcl Package initializer, from sha1.o 32 + */ 33 +int Sha1_Init(Tcl_Interp *interp); 34 + 35 +/* 36 + * Thread Specific Data (TSD) for Tcl Interpreter for the current thread 37 + */ 27 38 static pthread_key_t interpKey; 28 39 29 -struct appfs_thread_data { 30 - sqlite3 *db; 31 - const char *cachedir; 32 - time_t boottime; 33 - const char *platform; 34 - struct { 35 - int writable; 36 - } options; 37 -}; 40 +/* 41 + * Global variables, needed for all threads but only initialized before any 42 + * FUSE threads are created 43 + */ 44 +const char *appfs_cachedir; 45 +time_t appfs_boottime; 46 +int appfs_fuse_started = 0; 38 47 39 -struct appfs_thread_data globalThread; 40 - 48 +/* 49 + * AppFS Path Type: Describes the type of path a given file is 50 + */ 41 51 typedef enum { 42 52 APPFS_PATHTYPE_INVALID, 43 53 APPFS_PATHTYPE_FILE, 44 54 APPFS_PATHTYPE_DIRECTORY, 45 - APPFS_PATHTYPE_SYMLINK 55 + APPFS_PATHTYPE_SYMLINK, 56 + APPFS_PATHTYPE_SOCKET, 57 + APPFS_PATHTYPE_FIFO, 46 58 } appfs_pathtype_t; 47 59 48 -struct appfs_children { 49 - struct appfs_children *_next; 50 - int counter; 51 - 52 - char name[256]; 53 -}; 54 - 60 +/* 61 + * AppFS Path Information: 62 + * Completely describes a specific path, how it should be returned to 63 + * to the kernel 64 + */ 55 65 struct appfs_pathinfo { 56 66 appfs_pathtype_t type; 57 67 time_t time; 58 68 char hostname[256]; 59 69 int packaged; 60 70 unsigned long long inode; 61 71 union { 62 72 struct { 63 73 int childcount; 64 74 } dir; 65 75 struct { 66 76 int executable; 67 77 off_t size; 68 - char sha1[41]; 69 78 } file; 70 79 struct { 71 80 off_t size; 72 81 char source[256]; 73 82 } symlink; 74 83 } typeinfo; 75 84 }; 76 85 77 -struct appfs_sqlite3_query_cb_handle { 78 - struct appfs_children *head; 79 - int argc; 80 - const char *fmt; 81 -}; 82 - 83 -static Tcl_Interp *appfs_create_TclInterp(const char *cachedir) { 86 +/* 87 + * Create a new Tcl interpreter and completely initialize it 88 + */ 89 +static Tcl_Interp *appfs_create_TclInterp(char **error_string) { 84 90 Tcl_Interp *interp; 85 91 int tcl_ret; 86 92 87 93 APPFS_DEBUG("Creating new Tcl interpreter for TID = 0x%llx", (unsigned long long) pthread_self()); 88 94 89 95 interp = Tcl_CreateInterp(); 90 96 if (interp == NULL) { 91 97 fprintf(stderr, "Unable to create Tcl Interpreter. Aborting.\n"); 98 + 99 + if (error_string) { 100 + *error_string = strdup("Unable to create Tcl interpreter."); 101 + } 92 102 93 103 return(NULL); 94 104 } 95 105 96 106 tcl_ret = Tcl_Init(interp); 97 107 if (tcl_ret != TCL_OK) { 98 108 fprintf(stderr, "Unable to initialize Tcl. Aborting.\n"); 109 + fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp)); 110 + 111 + if (error_string) { 112 + *error_string = strdup(Tcl_GetStringResult(interp)); 113 + } 114 + 115 + Tcl_DeleteInterp(interp); 116 + 117 + return(NULL); 118 + } 119 + 120 + tcl_ret = Tcl_Eval(interp, "package ifneeded sha1 1.0 [list load {} sha1]"); 121 + if (tcl_ret != TCL_OK) { 122 + fprintf(stderr, "Unable to initialize Tcl SHA1. Aborting.\n"); 123 + fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp)); 124 + 125 + if (error_string) { 126 + *error_string = strdup(Tcl_GetStringResult(interp)); 127 + } 128 + 129 + Tcl_DeleteInterp(interp); 130 + 131 + return(NULL); 132 + } 133 + 134 + tcl_ret = Tcl_Eval(interp, "package ifneeded appfsd 1.0 [list load {} appfsd]"); 135 + if (tcl_ret != TCL_OK) { 136 + fprintf(stderr, "Unable to initialize Tcl AppFS Package. Aborting.\n"); 137 + fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp)); 138 + 139 + if (error_string) { 140 + *error_string = strdup(Tcl_GetStringResult(interp)); 141 + } 99 142 100 143 Tcl_DeleteInterp(interp); 101 144 102 145 return(NULL); 103 146 } 104 147 148 + /* 149 + * Load the "appfsd.tcl" script, which is "compiled" into a C header 150 + * so that it does not need to exist on the filesystem and can be 151 + * directly evaluated. 152 + */ 105 153 tcl_ret = Tcl_Eval(interp, "" 106 154 #include "appfsd.tcl.h" 107 155 ""); 108 156 if (tcl_ret != TCL_OK) { 109 157 fprintf(stderr, "Unable to initialize Tcl AppFS script. Aborting.\n"); 110 158 fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp)); 159 + 160 + if (error_string) { 161 + *error_string = strdup(Tcl_GetStringResult(interp)); 162 + } 111 163 112 164 Tcl_DeleteInterp(interp); 113 165 114 166 return(NULL); 115 167 } 116 168 117 - if (Tcl_SetVar(interp, "::appfs::cachedir", cachedir, TCL_GLOBAL_ONLY) == NULL) { 169 + /* 170 + * Set global variables from C to Tcl 171 + */ 172 + if (Tcl_SetVar(interp, "::appfs::cachedir", appfs_cachedir, TCL_GLOBAL_ONLY) == NULL) { 118 173 fprintf(stderr, "Unable to set cache directory. This should never fail.\n"); 174 + 175 + if (error_string) { 176 + *error_string = strdup(Tcl_GetStringResult(interp)); 177 + } 119 178 120 179 Tcl_DeleteInterp(interp); 121 180 122 181 return(NULL); 123 182 } 124 183 184 + /* 185 + * Initialize the "appfsd.tcl" environment, which must be done after 186 + * global variables are set. 187 + */ 125 188 tcl_ret = Tcl_Eval(interp, "::appfs::init"); 126 189 if (tcl_ret != TCL_OK) { 127 190 fprintf(stderr, "Unable to initialize Tcl AppFS script (::appfs::init). Aborting.\n"); 128 191 fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp)); 192 + 193 + if (error_string) { 194 + *error_string = strdup(Tcl_GetStringResult(interp)); 195 + } 129 196 130 197 Tcl_DeleteInterp(interp); 131 198 132 199 return(NULL); 133 200 } 134 201 135 - Tcl_HideCommand(interp, "glob", "glob"); 136 - Tcl_HideCommand(interp, "exec", "exec"); 137 - Tcl_HideCommand(interp, "pid", "pid"); 202 + /* 203 + * Hide some Tcl commands that we do not care to use and which may 204 + * slow down run-time operations. 205 + */ 138 206 Tcl_HideCommand(interp, "auto_load_index", "auto_load_index"); 139 207 Tcl_HideCommand(interp, "unknown", "unknown"); 208 + 209 + /* 210 + * Return the completely initialized interpreter 211 + */ 212 + return(interp); 213 +} 214 + 215 +/* 216 + * Return the thread-specific Tcl interpreter, creating it if needed 217 + */ 218 +static Tcl_Interp *appfs_TclInterp(void) { 219 + Tcl_Interp *interp; 220 + int pthread_ret; 221 + 222 + interp = pthread_getspecific(interpKey); 223 + if (interp == NULL) { 224 + interp = appfs_create_TclInterp(NULL); 225 + 226 + if (interp == NULL) { 227 + return(NULL); 228 + } 229 + 230 + pthread_ret = pthread_setspecific(interpKey, interp); 231 + if (pthread_ret != 0) { 232 + Tcl_DeleteInterp(interp); 233 + 234 + return(NULL); 235 + } 236 + } 140 237 141 238 return(interp); 142 239 } 143 240 241 +/* 242 + * Evaluate a Tcl script constructed by concatenating a bunch of C strings 243 + * together. 244 + */ 144 245 static int appfs_Tcl_Eval(Tcl_Interp *interp, int objc, const char *cmd, ...) { 145 246 Tcl_Obj **objv; 146 247 const char *arg; 147 248 va_list argp; 148 249 int retval; 149 250 int i; 150 251 ................................................................................ 175 276 if (retval != TCL_OK) { 176 277 APPFS_DEBUG("Tcl command failed, ::errorInfo contains: %s\n", Tcl_GetVar(interp, "::errorInfo", 0)); 177 278 } 178 279 179 280 return(retval); 180 281 } 181 282 182 -static void appfs_update_index(const char *hostname) { 183 - Tcl_Interp *interp; 184 - int tcl_ret; 185 - 186 - APPFS_DEBUG("Enter: hostname = %s", hostname); 187 - 188 - interp = pthread_getspecific(interpKey); 189 - if (interp == NULL) { 190 - interp = appfs_create_TclInterp(globalThread.cachedir); 191 - 192 - if (interp == NULL) { 193 - return; 194 - } 195 - 196 - pthread_setspecific(interpKey, interp); 197 - } 198 - 199 - tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getindex", hostname); 200 - if (tcl_ret != TCL_OK) { 201 - APPFS_DEBUG("Call to ::appfs::getindex failed: %s", Tcl_GetStringResult(interp)); 202 - 203 - return; 204 - } 205 - 206 - return; 207 -} 208 - 209 -static const char *appfs_getfile(const char *hostname, const char *sha1) { 210 - Tcl_Interp *interp; 211 - char *retval; 212 - int tcl_ret; 213 - 214 - interp = pthread_getspecific(interpKey); 215 - if (interp == NULL) { 216 - interp = appfs_create_TclInterp(globalThread.cachedir); 217 - 218 - if (interp == NULL) { 219 - return(NULL); 220 - } 221 - 222 - pthread_setspecific(interpKey, interp); 223 - } 224 - 225 - tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::download", hostname, sha1); 226 - if (tcl_ret != TCL_OK) { 227 - APPFS_DEBUG("Call to ::appfs::download failed: %s", Tcl_GetStringResult(interp)); 228 - 229 - return(NULL); 230 - } 231 - 232 - retval = strdup(Tcl_GetStringResult(interp)); 233 - 234 - return(retval); 235 -} 236 - 237 -static void appfs_update_manifest(const char *hostname, const char *sha1) { 238 - Tcl_Interp *interp; 239 - int tcl_ret; 240 - 241 - interp = pthread_getspecific(interpKey); 242 - if (interp == NULL) { 243 - interp = appfs_create_TclInterp(globalThread.cachedir); 244 - 245 - if (interp == NULL) { 246 - return; 247 - } 248 - 249 - pthread_setspecific(interpKey, interp); 250 - } 251 - 252 - tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::getpkgmanifest", hostname, sha1); 253 - if (tcl_ret != TCL_OK) { 254 - APPFS_DEBUG("Call to ::appfs::getpkgmanifest failed: %s", Tcl_GetStringResult(interp)); 255 - 256 - return; 257 - } 258 - 259 - return; 260 -} 261 - 262 -#define appfs_free_list_type(id, type) static void appfs_free_list_ ## id(type *head) { \ 263 - type *obj, *next; \ 264 - for (obj = head; obj; obj = next) { \ 265 - next = obj->_next; \ 266 - ckfree((void *) obj); \ 267 - } \ 268 -} 269 - 270 -appfs_free_list_type(children, struct appfs_children) 271 - 283 +/* 284 + * Determine the UID for the user making the current FUSE filesystem request. 285 + * This will be used to lookup the user's home directory so we can search for 286 + * locally modified files. 287 + */ 272 288 static uid_t appfs_get_fsuid(void) { 273 289 struct fuse_context *ctx; 290 + 291 + if (!appfs_fuse_started) { 292 + return(getuid()); 293 + } 274 294 275 295 ctx = fuse_get_context(); 276 296 if (ctx == NULL) { 297 + /* Unable to lookup user for some reason */ 298 + /* Return an unprivileged user ID */ 277 299 return(1); 278 300 } 279 301 280 302 return(ctx->uid); 281 303 } 282 304 283 -static const char *appfs_get_homedir(uid_t fsuid) { 305 +/* 306 + * Look up the home directory for a given UID 307 + * Returns a C string containing the user's home directory or NULL if 308 + * the user's home directory does not exist or is not correctly 309 + * configured 310 + */ 311 +static char *appfs_get_homedir(uid_t fsuid) { 284 312 struct passwd entry, *result; 285 313 struct stat stbuf; 286 314 char buf[1024], *retval; 287 315 int gpu_ret, stat_ret; 288 316 289 317 gpu_ret = getpwuid_r(fsuid, &entry, buf, sizeof(buf), &result); 290 318 if (gpu_ret != 0) { ................................................................................ 318 346 result->pw_dir, 319 347 (unsigned long long) stbuf.st_uid 320 348 ); 321 349 322 350 return(NULL); 323 351 } 324 352 325 - retval = sqlite3_mprintf("%s", result->pw_dir); 353 + retval = strdup(result->pw_dir); 326 354 327 355 return(retval); 328 356 } 329 357 330 -static int appfs_getpackage_name_cb(void *_package_name, int columns, char **values, char **names) { 331 - char **package_name; 332 - 333 - if (columns != 1) { 334 - return(1); 335 - } 336 - 337 - package_name = _package_name; 338 - 339 - *package_name = sqlite3_mprintf("%s", values[0]); 340 - 341 - return(0); 342 -} 343 - 344 -static char *appfs_getpackage_name(const char *hostname, const char *package_hash) { 345 - char *sql; 346 - int sqlite_ret; 347 - char *package_name = NULL; 348 - 349 - sql = sqlite3_mprintf("SELECT package FROM packages WHERE hostname = %Q AND sha1 = %Q LIMIT 1;", hostname, package_hash); 350 - if (sql == NULL) { 351 - APPFS_DEBUG("Call to sqlite3_mprintf failed."); 352 - 353 - return(sqlite3_mprintf("%s", "unknown-package-name")); 354 - } 355 - sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_getpackage_name_cb, &package_name, NULL); 356 - sqlite3_free(sql); 357 - 358 - if (sqlite_ret != SQLITE_OK) { 359 - APPFS_DEBUG("Call to sqlite3_exec failed."); 360 - 361 - return(sqlite3_mprintf("%s", "unknown-package-name")); 362 - } 363 - 364 - return(package_name); 365 -} 366 - 367 -static struct appfs_children *appfs_getchildren_fs(struct appfs_children *in_children, const char *fspath) { 368 - APPFS_DEBUG("Searching %s", fspath); 369 - 370 - return(in_children); 371 -} 372 - 373 -static int appfs_getchildren_cb(void *_head, int columns, char **values, char **names) { 374 - struct appfs_children **head_p, *obj; 375 - 376 - head_p = _head; 377 - 378 - obj = (void *) ckalloc(sizeof(*obj)); 379 - 380 - snprintf(obj->name, sizeof(obj->name), "%s", values[0]); 381 - 382 - if (*head_p == NULL) { 383 - obj->counter = 0; 384 - } else { 385 - obj->counter = (*head_p)->counter + 1; 386 - } 387 - 388 - obj->_next = *head_p; 389 - *head_p = obj; 390 - 391 - return(0); 392 - 393 -} 394 - 395 -static struct appfs_children *appfs_getchildren(const char *hostname, const char *package_hash, const char *path, int *children_count_p) { 396 - struct appfs_children *head = NULL; 397 - char *sql, *filebuf, *homedir = NULL; 398 - int sqlite_ret; 399 - uid_t fsuid; 400 - 401 - if (children_count_p == NULL) { 402 - return(NULL); 403 - } 404 - 405 - appfs_update_index(hostname); 406 - appfs_update_manifest(hostname, package_hash); 407 - 408 - sql = sqlite3_mprintf("SELECT file_name FROM files WHERE package_sha1 = %Q AND file_directory = %Q;", package_hash, path); 409 - if (sql == NULL) { 410 - APPFS_DEBUG("Call to sqlite3_mprintf failed."); 411 - 412 - return(NULL); 413 - } 414 - 415 - APPFS_DEBUG("SQL: %s", sql); 416 - sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_getchildren_cb, &head, NULL); 417 - sqlite3_free(sql); 418 - 419 - if (sqlite_ret != SQLITE_OK) { 420 - APPFS_DEBUG("Call to sqlite3_exec failed."); 421 - 422 - appfs_free_list_children(head); 423 - 424 - return(NULL); 425 - } 426 - 427 - if (globalThread.options.writable) { 428 - /* Determine user of process accessing this file */ 429 - fsuid = appfs_get_fsuid(); 430 - 431 - /* Check filesystem paths for updated files */ 432 - /** Check the global directory (/etc) **/ 433 - filebuf = sqlite3_mprintf("/etc/appfs/%z@%s/%s", appfs_getpackage_name(hostname, package_hash), hostname, path); 434 - if (filebuf == NULL) { 435 - APPFS_DEBUG("Call to sqlite3_mprintf failed."); 436 - 437 - return(NULL); 438 - } 439 - 440 - head = appfs_getchildren_fs(head, filebuf); 441 - 442 - sqlite3_free(filebuf); 443 - 444 - /** Check the user's directory, if we are not root **/ 445 - if (fsuid != 0) { 446 - homedir = (char *) appfs_get_homedir(fsuid); 447 - } 448 - 449 - if (homedir != NULL) { 450 - filebuf = sqlite3_mprintf("%z/.appfs/%z@%s/%s", homedir, appfs_getpackage_name(hostname, package_hash), hostname, path); 451 - 452 - if (filebuf == NULL) { 453 - APPFS_DEBUG("Call to sqlite3_mprintf failed."); 454 - 455 - return(NULL); 456 - } 457 - 458 - head = appfs_getchildren_fs(head, filebuf); 459 - 460 - sqlite3_free(filebuf); 461 - } 462 - } 463 - 464 - if (head != NULL) { 465 - *children_count_p = head->counter + 1; 466 - } else { 467 - *children_count_p = 0; 468 - } 469 - 470 - return(head); 471 -} 472 - 473 -static int appfs_sqlite3_query_cb(void *_cb_handle, int columns, char **values, char **names) { 474 - struct appfs_sqlite3_query_cb_handle *cb_handle; 475 - struct appfs_children *obj; 476 - 477 - cb_handle = _cb_handle; 478 - 479 - obj = (void *) ckalloc(sizeof(*obj)); 480 - 481 - switch (cb_handle->argc) { 482 - case 1: 483 - snprintf(obj->name, sizeof(obj->name), cb_handle->fmt, values[0]); 484 - break; 485 - case 2: 486 - snprintf(obj->name, sizeof(obj->name), cb_handle->fmt, values[0], values[1]); 487 - break; 488 - case 3: 489 - snprintf(obj->name, sizeof(obj->name), cb_handle->fmt, values[0], values[1], values[2]); 490 - break; 491 - case 4: 492 - snprintf(obj->name, sizeof(obj->name), cb_handle->fmt, values[0], values[1], values[2], values[3]); 493 - break; 494 - } 495 - 496 - if (cb_handle->head == NULL) { 497 - obj->counter = 0; 498 - } else { 499 - obj->counter = cb_handle->head->counter + 1; 500 - } 501 - 502 - obj->_next = cb_handle->head; 503 - cb_handle->head = obj; 504 - 505 - return(0); 506 -} 507 - 508 -static struct appfs_children *appfs_sqlite3_query(char *sql, int argc, const char *fmt, int *results_count_p) { 509 - struct appfs_sqlite3_query_cb_handle cb_handle; 510 - int sqlite_ret; 511 - 512 - if (results_count_p == NULL) { 513 - return(NULL); 514 - } 515 - 516 - if (sql == NULL) { 517 - APPFS_DEBUG("Call to sqlite3_mprintf probably failed."); 518 - 519 - return(NULL); 520 - } 521 - 522 - if (fmt == NULL) { 523 - fmt = "%s"; 524 - } 525 - 526 - cb_handle.head = NULL; 527 - cb_handle.argc = argc; 528 - cb_handle.fmt = fmt; 529 - 530 - APPFS_DEBUG("SQL: %s", sql); 531 - sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_sqlite3_query_cb, &cb_handle, NULL); 532 - sqlite3_free(sql); 533 - 534 - if (sqlite_ret != SQLITE_OK) { 535 - APPFS_DEBUG("Call to sqlite3_exec failed."); 536 - 537 - return(NULL); 538 - } 539 - 540 - if (cb_handle.head != NULL) { 541 - *results_count_p = cb_handle.head->counter + 1; 542 - } 543 - 544 - return(cb_handle.head); 545 -} 546 - 547 -static int appfs_lookup_package_hash_cb(void *_retval, int columns, char **values, char **names) { 548 - char **retval = _retval; 549 - 550 - *retval = strdup(values[0]); 551 - 552 - return(0); 553 -} 554 - 555 -static char *appfs_lookup_package_hash(const char *hostname, const char *package, const char *os, const char *cpuArch, const char *version) { 556 - char *sql; 557 - char *retval = NULL; 558 - int sqlite_ret; 559 - 560 - appfs_update_index(hostname); 561 - 562 - sql = sqlite3_mprintf("SELECT sha1 FROM packages WHERE hostname = %Q AND package = %Q AND os = %Q AND cpuArch = %Q AND version = %Q;", 563 - hostname, 564 - package, 565 - os, 566 - cpuArch, 567 - version 568 - ); 569 - if (sql == NULL) { 570 - APPFS_DEBUG("Call to sqlite3_mprintf failed."); 571 - 572 - return(NULL); 573 - } 574 - 575 - APPFS_DEBUG("SQL: %s", sql); 576 - sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_lookup_package_hash_cb, &retval, NULL); 577 - sqlite3_free(sql); 578 - 579 - if (sqlite_ret != SQLITE_OK) { 580 - APPFS_DEBUG("Call to sqlite3_exec failed."); 581 - 582 - return(NULL); 583 - } 584 - 585 - return(retval); 586 -} 587 - 588 -static int appfs_getfileinfo_cb(void *_pathinfo, int columns, char **values, char **names) { 589 - struct appfs_pathinfo *pathinfo = _pathinfo; 590 - const char *type, *time, *source, *size, *perms, *sha1, *rowid; 591 - 592 - type = values[0]; 593 - time = values[1]; 594 - source = values[2]; 595 - size = values[3]; 596 - perms = values[4]; 597 - sha1 = values[5]; 598 - rowid = values[6]; 599 - 600 - pathinfo->time = strtoull(time, NULL, 10); 601 - 602 - /* Package file inodes start at 2^32, fake inodes are before then */ 603 - pathinfo->inode = strtoull(rowid, NULL, 10) + 4294967296ULL; 604 - 605 - if (strcmp(type, "file") == 0) { 606 - pathinfo->type = APPFS_PATHTYPE_FILE; 607 - 608 - if (!size) { 609 - size = "0"; 610 - } 611 - 612 - if (!perms) { 613 - perms = ""; 614 - } 615 - 616 - if (!sha1) { 617 - sha1 = ""; 618 - } 619 - 620 - pathinfo->typeinfo.file.size = strtoull(size, NULL, 10); 621 - snprintf(pathinfo->typeinfo.file.sha1, sizeof(pathinfo->typeinfo.file.sha1), "%s", sha1); 622 - 623 - if (strcmp(perms, "x") == 0) { 624 - pathinfo->typeinfo.file.executable = 1; 625 - } else { 626 - pathinfo->typeinfo.file.executable = 0; 627 - } 628 - 629 - return(0); 630 - } 631 - 632 - if (strcmp(type, "directory") == 0) { 633 - pathinfo->type = APPFS_PATHTYPE_DIRECTORY; 634 - pathinfo->typeinfo.dir.childcount = 0; 635 - 636 - return(0); 637 - } 638 - 639 - if (strcmp(type, "symlink") == 0) { 640 - pathinfo->type = APPFS_PATHTYPE_SYMLINK; 641 - pathinfo->typeinfo.dir.childcount = 0; 642 - 643 - if (!source) { 644 - source = ".BADLINK"; 645 - } 646 - 647 - pathinfo->typeinfo.symlink.size = strlen(source); 648 - snprintf(pathinfo->typeinfo.symlink.source, sizeof(pathinfo->typeinfo.symlink.source), "%s", source); 649 - 650 - return(0); 651 - } 652 - 653 - return(0); 654 - 655 - /* Until this is used, prevent the compiler from complaining */ 656 - source = source; 657 -} 658 - 659 -static int appfs_getfileinfo(const char *hostname, const char *package_hash, const char *_path, struct appfs_pathinfo *pathinfo) { 660 - char *directory, *file, *path; 661 - char *sql; 662 - int sqlite_ret; 663 - 664 - if (pathinfo == NULL) { 665 - return(-EIO); 666 - } 667 - 668 - appfs_update_index(hostname); 669 - appfs_update_manifest(hostname, package_hash); 670 - 671 - path = strdup(_path); 672 - directory = path; 673 - file = strrchr(path, '/'); 674 - if (file == NULL) { 675 - file = path; 676 - directory = ""; 677 - } else { 678 - *file = '\0'; 679 - file++; 680 - } 681 - 682 - sql = sqlite3_mprintf("SELECT type, time, source, size, perms, file_sha1, rowid FROM files WHERE package_sha1 = %Q AND file_directory = %Q AND file_name = %Q;", package_hash, directory, file); 683 - if (sql == NULL) { 684 - APPFS_DEBUG("Call to sqlite3_mprintf failed."); 685 - 686 - free(path); 687 - 688 - return(-EIO); 689 - } 690 - 691 - free(path); 692 - 693 - pathinfo->type = APPFS_PATHTYPE_INVALID; 694 - 695 - APPFS_DEBUG("SQL: %s", sql); 696 - sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_getfileinfo_cb, pathinfo, NULL); 697 - sqlite3_free(sql); 698 - 699 - if (sqlite_ret != SQLITE_OK) { 700 - APPFS_DEBUG("Call to sqlite3_exec failed."); 701 - 702 - return(-EIO); 703 - } 704 - 705 - if (pathinfo->type == APPFS_PATHTYPE_INVALID) { 706 - return(-ENOENT); 707 - } 708 - 709 - return(0); 710 -} 711 - 712 -static int appfs_get_path_info_sql(char *sql, int argc, const char *fmt, struct appfs_pathinfo *pathinfo, struct appfs_children **children) { 713 - struct appfs_children *node, *dir_children, *dir_child; 714 - int dir_children_count = 0; 715 - 716 - dir_children = appfs_sqlite3_query(sql, argc, fmt, &dir_children_count); 717 - 718 - if (dir_children == NULL || dir_children_count == 0) { 719 - return(-ENOENT); 720 - } 721 - 722 - /* Request for a single hostname */ 723 - pathinfo->type = APPFS_PATHTYPE_DIRECTORY; 724 - pathinfo->typeinfo.dir.childcount = dir_children_count; 725 - pathinfo->time = globalThread.boottime; 726 - 727 - if (children) { 728 - for (dir_child = dir_children; dir_child; dir_child = dir_child->_next) { 729 - node = (void *) ckalloc(sizeof(*node)); 730 - node->_next = *children; 731 - strcpy(node->name, dir_child->name); 732 - *children = node; 733 - } 734 - } 735 - 736 - appfs_free_list_children(dir_children); 737 - 738 - return(0); 739 -} 740 - 741 -static int appfs_add_path_child(const char *name, struct appfs_pathinfo *pathinfo, struct appfs_children **children) { 742 - struct appfs_children *new_child; 743 - 744 - pathinfo->typeinfo.dir.childcount++; 745 - 746 - if (children) { 747 - new_child = (void *) ckalloc(sizeof(*new_child)); 748 - new_child->_next = *children; 749 - 750 - snprintf(new_child->name, sizeof(new_child->name), "%s", name); 751 - 752 - *children = new_child; 753 - } 754 - 755 - return(0); 756 -} 757 - 758 -/* Generate an inode for a given path */ 358 +/* 359 + * Tcl interface to get the home directory for the user making the "current" 360 + * FUSE I/O request 361 + */ 362 +static int tcl_appfs_get_homedir(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { 363 + char *homedir; 364 + 365 + if (objc != 1) { 366 + Tcl_WrongNumArgs(interp, 1, objv, NULL); 367 + return(TCL_ERROR); 368 + } 369 + 370 + homedir = appfs_get_homedir(appfs_get_fsuid()); 371 + 372 + if (homedir == NULL) { 373 + return(TCL_ERROR); 374 + } 375 + 376 + Tcl_SetObjResult(interp, Tcl_NewStringObj(homedir, -1)); 377 + 378 + free(homedir); 379 + 380 + return(TCL_OK); 381 +} 382 + 383 +/* 384 + * Generate an inode for a given path. The inode should be computed in such 385 + * a way that it is unlikely to be duplicated and remains the same for a given 386 + * file 387 + */ 759 388 static long long appfs_get_path_inode(const char *path) { 760 389 long long retval; 761 390 const char *p; 762 391 763 392 retval = 10; 764 393 765 394 for (p = path; *p; p++) { ................................................................................ 771 400 retval += 10; 772 401 retval %= 4294967296ULL; 773 402 774 403 return(retval); 775 404 } 776 405 777 406 /* Get information about a path, and optionally list children */ 778 -static int appfs_get_path_info(const char *_path, struct appfs_pathinfo *pathinfo, struct appfs_children **children) { 779 - struct appfs_children *dir_children; 780 - char *hostname, *packagename, *os_cpuArch, *os, *cpuArch, *version; 781 - char *path, *path_s; 782 - char *package_hash; 783 - char *sql; 784 - int files_count; 785 - int fileinfo_ret, retval; 786 - 787 - /* Initialize return */ 788 - if (children) { 789 - *children = NULL; 790 - } 791 - 792 - /* Verify that this is a valid request */ 793 - if (_path == NULL) { 794 - return(-ENOENT); 795 - } 796 - 797 - if (_path[0] != '/') { 798 - return(-ENOENT); 799 - } 800 - 801 - /* Note that this is not a "real" directory from a package */ 802 - pathinfo->packaged = 0; 803 - 804 - if (_path[1] == '\0') { 805 - /* Request for the root directory */ 806 - pathinfo->hostname[0] = '\0'; 807 - pathinfo->inode = 1; 808 - 809 - sql = sqlite3_mprintf("SELECT DISTINCT hostname FROM packages;"); 810 - 811 - retval = appfs_get_path_info_sql(sql, 1, NULL, pathinfo, children); 812 - 813 - /* The root directory always exists, even if it has no subordinates */ 814 - if (retval != 0) { 815 - pathinfo->type = APPFS_PATHTYPE_DIRECTORY; 816 - pathinfo->typeinfo.dir.childcount = 0; 817 - pathinfo->time = globalThread.boottime; 818 - 819 - retval = 0; 820 - } 821 - 822 - return(retval); 823 - } 824 - 825 - path = strdup(_path); 826 - path_s = path; 827 - 828 - pathinfo->inode = appfs_get_path_inode(path); 829 - 830 - hostname = path + 1; 831 - packagename = strchr(hostname, '/'); 832 - 833 - if (packagename != NULL) { 834 - *packagename = '\0'; 835 - packagename++; 836 - } 837 - 838 - snprintf(pathinfo->hostname, sizeof(pathinfo->hostname), "%s", hostname); 839 - 840 - if (packagename == NULL) { 841 - appfs_update_index(hostname); 842 - 843 - sql = sqlite3_mprintf("SELECT DISTINCT package FROM packages WHERE hostname = %Q;", hostname); 844 - 845 - free(path_s); 846 - 847 - return(appfs_get_path_info_sql(sql, 1, NULL, pathinfo, children)); 848 - } 849 - 850 - os_cpuArch = strchr(packagename, '/'); 851 - 852 - if (os_cpuArch != NULL) { 853 - *os_cpuArch = '\0'; 854 - os_cpuArch++; 855 - } 856 - 857 - if (os_cpuArch == NULL) { 858 - appfs_update_index(hostname); 859 - 860 - sql = sqlite3_mprintf("SELECT DISTINCT os, cpuArch FROM packages WHERE hostname = %Q AND package = %Q;", hostname, packagename); 861 - 862 - free(path_s); 863 - 864 - retval = appfs_get_path_info_sql(sql, 2, "%s-%s", pathinfo, children); 865 - 866 - if (retval != 0) { 867 - return(retval); 868 - } 869 - 870 - appfs_add_path_child("platform", pathinfo, children); 871 - 872 - return(retval); 873 - } 874 - 875 - version = strchr(os_cpuArch, '/'); 876 - 877 - if (version != NULL) { 878 - *version = '\0'; 879 - version++; 880 - } 881 - 882 - os = os_cpuArch; 883 - cpuArch = strchr(os_cpuArch, '-'); 884 - if (cpuArch) { 885 - *cpuArch = '\0'; 886 - cpuArch++; 887 - } else { 888 - cpuArch = ""; 889 - } 890 - 891 - if (version == NULL) { 892 - if (strcmp(os, "platform") == 0 && strcmp(cpuArch, "") == 0) { 893 - pathinfo->type = APPFS_PATHTYPE_SYMLINK; 894 - pathinfo->time = globalThread.boottime; 895 - pathinfo->typeinfo.dir.childcount = 0; 896 - pathinfo->typeinfo.symlink.size = strlen(globalThread.platform); 897 - 898 - snprintf(pathinfo->typeinfo.symlink.source, sizeof(pathinfo->typeinfo.symlink.source), "%s", globalThread.platform); 899 - 900 - free(path_s); 901 - 902 - return(0); 903 - } 904 - 905 - /* Request for version list for a package on an OS/CPU */ 906 - appfs_update_index(hostname); 907 - 908 - sql = sqlite3_mprintf("SELECT DISTINCT version FROM packages WHERE hostname = %Q AND package = %Q AND os = %Q and cpuArch = %Q;", hostname, packagename, os, cpuArch); 909 - 910 - free(path_s); 911 - 912 - return(appfs_get_path_info_sql(sql, 1, NULL, pathinfo, children)); 913 - } 914 - 915 - path = strchr(version, '/'); 916 - if (path == NULL) { 917 - path = ""; 918 - } else { 919 - *path = '\0'; 920 - path++; 921 - } 922 - 923 - /* Request for a file in a specific package */ 924 - pathinfo->packaged = 1; 925 - APPFS_DEBUG("Requesting information for hostname = %s, package = %s, os = %s, cpuArch = %s, version = %s, path = %s", 926 - hostname, packagename, os, cpuArch, version, path 927 - ); 928 - 929 - package_hash = appfs_lookup_package_hash(hostname, packagename, os, cpuArch, version); 930 - if (package_hash == NULL) { 931 - free(path_s); 407 +static int appfs_get_path_info(const char *path, struct appfs_pathinfo *pathinfo) { 408 + Tcl_Interp *interp; 409 + Tcl_Obj *attrs_dict, *attr_value; 410 + const char *attr_value_str; 411 + Tcl_WideInt attr_value_wide; 412 + int attr_value_int; 413 + 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; 414 + int tcl_ret; 415 + 416 + interp = appfs_TclInterp(); 417 + if (interp == NULL) { 418 + return(-EIO); 419 + } 420 + 421 + tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getattr", path); 422 + if (tcl_ret != TCL_OK) { 423 + APPFS_DEBUG("::appfs::getattr(%s) failed.", path); 424 + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); 932 425 933 426 return(-ENOENT); 934 427 } 935 428 936 - APPFS_DEBUG(" ... which hash a hash of %s", package_hash); 937 - 938 - appfs_update_manifest(hostname, package_hash); 939 - 940 - if (strcmp(path, "") == 0) { 941 - pathinfo->type = APPFS_PATHTYPE_DIRECTORY; 942 - pathinfo->time = globalThread.boottime; 943 - } else { 944 - fileinfo_ret = appfs_getfileinfo(hostname, package_hash, path, pathinfo); 945 - if (fileinfo_ret != 0) { 946 - free(path_s); 947 - 948 - return(fileinfo_ret); 949 - } 950 - } 951 - 952 - if (pathinfo->type == APPFS_PATHTYPE_DIRECTORY) { 953 - dir_children = appfs_getchildren(hostname, package_hash, path, &files_count); 954 - 955 - if (dir_children != NULL) { 956 - pathinfo->typeinfo.dir.childcount = files_count; 429 + if (attr_key_type == NULL) { 430 + attr_key_type = Tcl_NewStringObj("type", -1); 431 + attr_key_perms = Tcl_NewStringObj("perms", -1); 432 + attr_key_size = Tcl_NewStringObj("size", -1); 433 + attr_key_time = Tcl_NewStringObj("time", -1); 434 + attr_key_source = Tcl_NewStringObj("source", -1); 435 + attr_key_childcount = Tcl_NewStringObj("childcount", -1); 436 + attr_key_packaged = Tcl_NewStringObj("packaged", -1); 437 + } 438 + 439 + attrs_dict = Tcl_GetObjResult(interp); 440 + tcl_ret = Tcl_DictObjGet(interp, attrs_dict, attr_key_type, &attr_value); 441 + if (tcl_ret != TCL_OK) { 442 + APPFS_DEBUG("[dict get \"type\"] failed"); 443 + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); 444 + 445 + return(-EIO); 446 + } 447 + 448 + if (attr_value == NULL) { 449 + return(-EIO); 450 + } 451 + 452 + pathinfo->packaged = 0; 453 + pathinfo->inode = appfs_get_path_inode(path); 454 + 455 + attr_value_str = Tcl_GetString(attr_value); 456 + switch (attr_value_str[0]) { 457 + case 'd': /* directory */ 458 + pathinfo->type = APPFS_PATHTYPE_DIRECTORY; 459 + pathinfo->typeinfo.dir.childcount = 0; 460 + 461 + Tcl_DictObjGet(interp, attrs_dict, attr_key_childcount, &attr_value); 462 + if (attr_value != NULL) { 463 + tcl_ret = Tcl_GetWideIntFromObj(NULL, attr_value, &attr_value_wide); 464 + if (tcl_ret == TCL_OK) { 465 + pathinfo->typeinfo.dir.childcount = attr_value_wide; 466 + } 467 + } 468 + 469 + break; 470 + case 'f': /* file */ 471 + pathinfo->type = APPFS_PATHTYPE_FILE; 472 + pathinfo->typeinfo.file.size = 0; 473 + pathinfo->typeinfo.file.executable = 0; 474 + 475 + Tcl_DictObjGet(interp, attrs_dict, attr_key_size, &attr_value); 476 + if (attr_value != NULL) { 477 + tcl_ret = Tcl_GetWideIntFromObj(NULL, attr_value, &attr_value_wide); 478 + if (tcl_ret == TCL_OK) { 479 + pathinfo->typeinfo.file.size = attr_value_wide; 480 + } 481 + } 482 + 483 + Tcl_DictObjGet(interp, attrs_dict, attr_key_perms, &attr_value); 484 + if (attr_value != NULL) { 485 + attr_value_str = Tcl_GetString(attr_value); 486 + if (attr_value_str[0] == 'x') { 487 + pathinfo->typeinfo.file.executable = 1; 488 + } 489 + } 490 + break; 491 + case 's': /* symlink */ 492 + pathinfo->type = APPFS_PATHTYPE_SYMLINK; 493 + pathinfo->typeinfo.symlink.size = 0; 494 + pathinfo->typeinfo.symlink.source[0] = '\0'; 495 + 496 + Tcl_DictObjGet(interp, attrs_dict, attr_key_source, &attr_value); 497 + if (attr_value != NULL) { 498 + attr_value_str = Tcl_GetStringFromObj(attr_value, &attr_value_int); 499 + 500 + if ((attr_value_int + 1) <= sizeof(pathinfo->typeinfo.symlink.source)) { 501 + pathinfo->typeinfo.symlink.size = attr_value_int; 502 + pathinfo->typeinfo.symlink.source[attr_value_int] = '\0'; 503 + 504 + memcpy(pathinfo->typeinfo.symlink.source, attr_value_str, attr_value_int); 505 + } 506 + } 507 + break; 508 + case 'p': /* pipe/fifo */ 509 + break; 510 + case 'S': /* UNIX domain socket */ 511 + break; 512 + default: 513 + return(-EIO); 514 + } 515 + 516 + Tcl_DictObjGet(interp, attrs_dict, attr_key_packaged, &attr_value); 517 + if (attr_value != NULL) { 518 + pathinfo->packaged = 1; 519 + } 520 + 521 + Tcl_DictObjGet(interp, attrs_dict, attr_key_time, &attr_value); 522 + if (attr_value != NULL) { 523 + tcl_ret = Tcl_GetWideIntFromObj(NULL, attr_value, &attr_value_wide); 524 + if (tcl_ret == TCL_OK) { 525 + pathinfo->time = attr_value_wide; 957 526 } 958 - 959 - if (children) { 960 - *children = dir_children; 961 - } else { 962 - appfs_free_list_children(dir_children); 963 - } 527 + } else { 528 + pathinfo->time = 0; 964 529 } 965 530 966 - free(path_s); 967 - 968 531 return(0); 969 532 } 533 + 534 +static char *appfs_prepare_to_create(const char *path) { 535 + Tcl_Interp *interp; 536 + const char *real_path; 537 + int tcl_ret; 538 + 539 + interp = appfs_TclInterp(); 540 + if (interp == NULL) { 541 + return(NULL); 542 + } 543 + 544 + tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::prepare_to_create", path); 545 + if (tcl_ret != TCL_OK) { 546 + APPFS_DEBUG("::appfs::prepare_to_create(%s) failed.", path); 547 + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); 548 + 549 + return(NULL); 550 + } 551 + 552 + real_path = Tcl_GetStringResult(interp); 553 + if (real_path == NULL) { 554 + return(NULL); 555 + } 556 + 557 + return(strdup(real_path)); 558 +} 559 + 560 +static char *appfs_localpath(const char *path) { 561 + Tcl_Interp *interp; 562 + const char *real_path; 563 + int tcl_ret; 564 + 565 + interp = appfs_TclInterp(); 566 + if (interp == NULL) { 567 + return(NULL); 568 + } 569 + 570 + tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::localpath", path); 571 + if (tcl_ret != TCL_OK) { 572 + APPFS_DEBUG("::appfs::localpath(%s) failed.", path); 573 + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); 574 + 575 + return(NULL); 576 + } 577 + 578 + real_path = Tcl_GetStringResult(interp); 579 + if (real_path == NULL) { 580 + return(NULL); 581 + } 582 + 583 + return(strdup(real_path)); 584 +} 970 585 971 586 static int appfs_fuse_readlink(const char *path, char *buf, size_t size) { 972 587 struct appfs_pathinfo pathinfo; 973 - int res = 0; 588 + int retval = 0; 974 589 975 590 APPFS_DEBUG("Enter (path = %s, ...)", path); 976 591 977 592 pathinfo.type = APPFS_PATHTYPE_INVALID; 978 593 979 - res = appfs_get_path_info(path, &pathinfo, NULL); 980 - if (res != 0) { 981 - return(res); 594 + retval = appfs_get_path_info(path, &pathinfo); 595 + if (retval != 0) { 596 + return(retval); 982 597 } 983 598 984 599 if (pathinfo.type != APPFS_PATHTYPE_SYMLINK) { 985 600 return(-EINVAL); 986 601 } 987 602 988 603 if ((strlen(pathinfo.typeinfo.symlink.source) + 1) > size) { ................................................................................ 992 607 memcpy(buf, pathinfo.typeinfo.symlink.source, strlen(pathinfo.typeinfo.symlink.source) + 1); 993 608 994 609 return(0); 995 610 } 996 611 997 612 static int appfs_fuse_getattr(const char *path, struct stat *stbuf) { 998 613 struct appfs_pathinfo pathinfo; 999 - int res = 0; 614 + int retval; 615 + 616 + retval = 0; 1000 617 1001 618 APPFS_DEBUG("Enter (path = %s, ...)", path); 1002 619 1003 620 pathinfo.type = APPFS_PATHTYPE_INVALID; 1004 621 1005 - res = appfs_get_path_info(path, &pathinfo, NULL); 1006 - if (res != 0) { 1007 - return(res); 622 + retval = appfs_get_path_info(path, &pathinfo); 623 + if (retval != 0) { 624 + return(retval); 1008 625 } 1009 626 1010 627 memset(stbuf, 0, sizeof(struct stat)); 1011 628 1012 629 stbuf->st_mtime = pathinfo.time; 1013 630 stbuf->st_ctime = pathinfo.time; 1014 631 stbuf->st_atime = pathinfo.time; 1015 632 stbuf->st_ino = pathinfo.inode; 1016 633 stbuf->st_mode = 0; 634 + stbuf->st_uid = appfs_get_fsuid(); 1017 635 1018 636 switch (pathinfo.type) { 1019 637 case APPFS_PATHTYPE_DIRECTORY: 1020 638 stbuf->st_mode = S_IFDIR | 0555; 1021 639 stbuf->st_nlink = 2 + pathinfo.typeinfo.dir.childcount; 1022 640 break; 1023 641 case APPFS_PATHTYPE_FILE: ................................................................................ 1031 649 stbuf->st_size = pathinfo.typeinfo.file.size; 1032 650 break; 1033 651 case APPFS_PATHTYPE_SYMLINK: 1034 652 stbuf->st_mode = S_IFLNK | 0555; 1035 653 stbuf->st_nlink = 1; 1036 654 stbuf->st_size = pathinfo.typeinfo.symlink.size; 1037 655 break; 656 + case APPFS_PATHTYPE_SOCKET: 657 + case APPFS_PATHTYPE_FIFO: 1038 658 case APPFS_PATHTYPE_INVALID: 1039 - res = -EIO; 659 + retval = -ENOENT; 1040 660 1041 661 break; 1042 662 } 1043 663 1044 664 if (pathinfo.packaged) { 1045 - if (globalThread.options.writable) { 1046 - stbuf->st_mode |= 0222; 1047 - } 665 + stbuf->st_mode |= 0222; 1048 666 } 1049 667 1050 - return res; 668 + return(retval); 1051 669 } 1052 670 1053 671 static int appfs_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) { 1054 - struct appfs_pathinfo pathinfo; 1055 - struct appfs_children *children, *child; 1056 - int retval; 672 + Tcl_Interp *interp; 673 + Tcl_Obj **children; 674 + int children_count, idx; 675 + int tcl_ret; 1057 676 1058 677 APPFS_DEBUG("Enter (path = %s, ...)", path); 1059 678 1060 - retval = appfs_get_path_info(path, &pathinfo, &children); 1061 - if (retval != 0) { 1062 - return(retval); 679 + interp = appfs_TclInterp(); 680 + if (interp == NULL) { 681 + return(0); 1063 682 } 1064 683 1065 684 filler(buf, ".", NULL, 0); 1066 685 filler(buf, "..", NULL, 0); 1067 686 1068 - for (child = children; child; child = child->_next) { 1069 - filler(buf, child->name, NULL, 0); 687 + tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getchildren", path); 688 + if (tcl_ret != TCL_OK) { 689 + APPFS_DEBUG("::appfs::getchildren(%s) failed.", path); 690 + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); 691 + 692 + return(0); 1070 693 } 1071 694 1072 - appfs_free_list_children(children); 695 + tcl_ret = Tcl_ListObjGetElements(interp, Tcl_GetObjResult(interp), &children_count, &children); 696 + if (tcl_ret != TCL_OK) { 697 + APPFS_DEBUG("Parsing list of children on path %s failed.", path); 698 + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); 699 + 700 + return(0); 701 + } 702 + 703 + for (idx = 0; idx < children_count; idx++) { 704 + filler(buf, Tcl_GetString(children[idx]), NULL, 0); 705 + } 1073 706 1074 707 return(0); 1075 708 } 1076 709 1077 710 static int appfs_fuse_open(const char *path, struct fuse_file_info *fi) { 711 + Tcl_Interp *interp; 1078 712 struct appfs_pathinfo pathinfo; 1079 - const char *real_path; 713 + const char *real_path, *mode; 714 + int gpi_ret, tcl_ret; 1080 715 int fh; 1081 - int gpi_ret; 1082 716 1083 717 APPFS_DEBUG("Enter (path = %s, ...)", path); 1084 718 1085 - if ((fi->flags & 3) != O_RDONLY) { 1086 - return(-EACCES); 1087 - } 719 + gpi_ret = appfs_get_path_info(path, &pathinfo); 1088 720 1089 - gpi_ret = appfs_get_path_info(path, &pathinfo, NULL); 1090 - if (gpi_ret != 0) { 1091 - return(gpi_ret); 721 + if ((fi->flags & (O_WRONLY|O_CREAT)) == (O_CREAT|O_WRONLY)) { 722 + /* The file will be created if it does not exist */ 723 + if (gpi_ret != 0 && gpi_ret != -ENOENT) { 724 + return(gpi_ret); 725 + } 726 + 727 + mode = "create"; 728 + } else { 729 + /* The file must already exist */ 730 + if (gpi_ret != 0) { 731 + return(gpi_ret); 732 + } 733 + 734 + mode = ""; 735 + 736 + if ((fi->flags & O_WRONLY) == O_WRONLY) { 737 + mode = "write"; 738 + } 1092 739 } 1093 740 1094 741 if (pathinfo.type == APPFS_PATHTYPE_DIRECTORY) { 1095 742 return(-EISDIR); 1096 743 } 1097 744 1098 - real_path = appfs_getfile(pathinfo.hostname, pathinfo.typeinfo.file.sha1); 745 + interp = appfs_TclInterp(); 746 + if (interp == NULL) { 747 + return(-EIO); 748 + } 749 + 750 + tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::openpath", path, mode); 751 + if (tcl_ret != TCL_OK) { 752 + APPFS_DEBUG("::appfs::openpath(%s, %s) failed.", path, mode); 753 + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); 754 + 755 + return(-EIO); 756 + } 757 + 758 + real_path = Tcl_GetStringResult(interp); 1099 759 if (real_path == NULL) { 1100 760 return(-EIO); 1101 761 } 1102 762 1103 - fh = open(real_path, O_RDONLY); 1104 - free((void *) real_path); 763 + APPFS_DEBUG("Translated request to open %s to opening %s (mode = \"%s\")", path, real_path, mode); 764 + 765 + fh = open(real_path, fi->flags, 0600); 1105 766 if (fh < 0) { 1106 767 return(-EIO); 1107 768 } 1108 769 1109 770 fi->fh = fh; 1110 771 1111 772 return(0); ................................................................................ 1134 795 } 1135 796 1136 797 read_ret = read(fi->fh, buf, size); 1137 798 1138 799 return(read_ret); 1139 800 } 1140 801 1141 -static struct fuse_operations appfs_oper = { 802 +static int appfs_fuse_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { 803 + off_t lseek_ret; 804 + ssize_t write_ret; 805 + 806 + APPFS_DEBUG("Enter (path = %s, ...)", path); 807 + 808 + lseek_ret = lseek(fi->fh, offset, SEEK_SET); 809 + if (lseek_ret != offset) { 810 + return(-EIO); 811 + } 812 + 813 + write_ret = write(fi->fh, buf, size); 814 + 815 + return(write_ret); 816 +} 817 + 818 +static int appfs_fuse_mknod(const char *path, mode_t mode, dev_t device) { 819 + char *real_path; 820 + int mknod_ret; 821 + 822 + APPFS_DEBUG("Enter (path = %s, ...)", path); 823 + 824 + if ((mode & S_IFCHR) == S_IFCHR) { 825 + return(-EPERM); 826 + } 827 + 828 + if ((mode & S_IFBLK) == S_IFBLK) { 829 + return(-EPERM); 830 + } 831 + 832 + real_path = appfs_prepare_to_create(path); 833 + if (real_path == NULL) { 834 + return(-EIO); 835 + } 836 + 837 + mknod_ret = mknod(real_path, mode, device); 838 + 839 + free(real_path); 840 + 841 + if (mknod_ret != 0) { 842 + return(errno * -1); 843 + } 844 + 845 + return(0); 846 +} 847 + 848 +static int appfs_fuse_create(const char *path, mode_t mode, struct fuse_file_info *fi) { 849 + char *real_path; 850 + int fd; 851 + 852 + APPFS_DEBUG("Enter (path = %s, ...)", path); 853 + 854 + if ((mode & S_IFCHR) == S_IFCHR) { 855 + return(-EPERM); 856 + } 857 + 858 + if ((mode & S_IFBLK) == S_IFBLK) { 859 + return(-EPERM); 860 + } 861 + 862 + real_path = appfs_prepare_to_create(path); 863 + if (real_path == NULL) { 864 + return(-EIO); 865 + } 866 + 867 + fd = creat(real_path, mode); 868 + 869 + free(real_path); 870 + 871 + if (fd < 0) { 872 + return(errno * -1); 873 + } 874 + 875 + fi->fh = fd; 876 + 877 + return(0); 878 +} 879 + 880 +static int appfs_fuse_truncate(const char *path, off_t size) { 881 + char *real_path; 882 + int truncate_ret; 883 + 884 + APPFS_DEBUG("Enter (path = %s, ...)", path); 885 + 886 + real_path = appfs_localpath(path); 887 + if (real_path == NULL) { 888 + return(-EIO); 889 + } 890 + 891 + truncate_ret = truncate(real_path, size); 892 + 893 + free(real_path); 894 + 895 + if (truncate_ret != 0) { 896 + return(errno * -1); 897 + } 898 + 899 + return(0); 900 +} 901 + 902 +static int appfs_fuse_unlink_rmdir(const char *path) { 903 + Tcl_Interp *interp; 904 + int tcl_ret; 905 + 906 + APPFS_DEBUG("Enter (path = %s, ...)", path); 907 + 908 + interp = appfs_TclInterp(); 909 + if (interp == NULL) { 910 + return(-EIO); 911 + } 912 + 913 + tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::unlinkpath", path); 914 + if (tcl_ret != TCL_OK) { 915 + APPFS_DEBUG("::appfs::unlinkpath(%s) failed.", path); 916 + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); 917 + 918 + return(-EIO); 919 + } 920 + 921 + return(0); 922 +} 923 + 924 +static int appfs_fuse_mkdir(const char *path, mode_t mode) { 925 + char *real_path; 926 + int mkdir_ret; 927 + 928 + APPFS_DEBUG("Enter (path = %s, ...)", path); 929 + 930 + real_path = appfs_prepare_to_create(path); 931 + if (real_path == NULL) { 932 + return(-EIO); 933 + } 934 + 935 + mkdir_ret = mkdir(real_path, mode); 936 + 937 + free(real_path); 938 + 939 + if (mkdir_ret != 0) { 940 + if (errno != EEXIST) { 941 + return(errno * -1); 942 + } 943 + } 944 + 945 + return(0); 946 +} 947 + 948 +static int appfs_fuse_chmod(const char *path, mode_t mode) { 949 + Tcl_Interp *interp; 950 + const char *real_path; 951 + int tcl_ret, chmod_ret; 952 + 953 + APPFS_DEBUG("Enter (path = %s, ...)", path); 954 + 955 + interp = appfs_TclInterp(); 956 + if (interp == NULL) { 957 + return(-EIO); 958 + } 959 + 960 + tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::openpath", path, "write"); 961 + if (tcl_ret != TCL_OK) { 962 + APPFS_DEBUG("::appfs::openpath(%s, %s) failed.", path, "write"); 963 + APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp)); 964 + 965 + return(-EIO); 966 + } 967 + 968 + real_path = Tcl_GetStringResult(interp); 969 + if (real_path == NULL) { 970 + return(-EIO); 971 + } 972 + 973 + chmod_ret = chmod(real_path, mode); 974 + 975 + return(chmod_ret); 976 +} 977 + 978 +/* 979 + * SQLite3 mode: Execute raw SQL and return success or failure 980 + */ 981 +static int appfs_sqlite3(const char *sql) { 982 + Tcl_Interp *interp; 983 + const char *sql_ret; 984 + int tcl_ret; 985 + 986 + interp = appfs_create_TclInterp(NULL); 987 + if (interp == NULL) { 988 + fprintf(stderr, "Unable to create a Tcl interpreter. Aborting.\n"); 989 + 990 + return(1); 991 + } 992 + 993 + tcl_ret = appfs_Tcl_Eval(interp, 5, "::appfs::db", "eval", sql, "row", "unset -nocomplain row(*); parray row; puts \"----\""); 994 + sql_ret = Tcl_GetStringResult(interp); 995 + 996 + if (tcl_ret != TCL_OK) { 997 + fprintf(stderr, "[error] %s\n", sql_ret); 998 + 999 + return(1); 1000 + } 1001 + 1002 + if (sql_ret && sql_ret[0] != '\0') { 1003 + printf("%s\n", sql_ret); 1004 + } 1005 + 1006 + return(0); 1007 +} 1008 + 1009 +/* 1010 + * Tcl mode: Execute raw Tcl and return success or failure 1011 + */ 1012 +static int appfs_tcl(const char *tcl) { 1013 + Tcl_Interp *interp; 1014 + const char *tcl_result; 1015 + int tcl_ret; 1016 + 1017 + interp = appfs_create_TclInterp(NULL); 1018 + if (interp == NULL) { 1019 + fprintf(stderr, "Unable to create a Tcl interpreter. Aborting.\n"); 1020 + 1021 + return(1); 1022 + } 1023 + 1024 + tcl_ret = Tcl_Eval(interp, tcl); 1025 + tcl_result = Tcl_GetStringResult(interp); 1026 + 1027 + if (tcl_ret != TCL_OK) { 1028 + fprintf(stderr, "[error] %s\n", tcl_result); 1029 + 1030 + return(1); 1031 + } 1032 + 1033 + if (tcl_result && tcl_result[0] != '\0') { 1034 + printf("%s\n", tcl_result); 1035 + } 1036 + 1037 + return(0); 1038 +} 1039 + 1040 +/* 1041 + * AppFSd Package for Tcl: 1042 + * Bridge for I/O operations to request information about the current 1043 + * transaction 1044 + */ 1045 +static int Appfsd_Init(Tcl_Interp *interp) { 1046 +#ifdef USE_TCL_STUBS 1047 + if (Tcl_InitStubs(interp, TCL_VERSION, 0) == 0L) { 1048 + return(TCL_ERROR); 1049 + } 1050 +#endif 1051 + 1052 + Tcl_CreateObjCommand(interp, "appfsd::get_homedir", tcl_appfs_get_homedir, NULL, NULL); 1053 + 1054 + Tcl_PkgProvide(interp, "appfsd", "1.0"); 1055 + 1056 + return(TCL_OK); 1057 +} 1058 + 1059 +/* 1060 + * FUSE operations structure 1061 + */ 1062 +static struct fuse_operations appfs_operations = { 1142 1063 .getattr = appfs_fuse_getattr, 1143 1064 .readdir = appfs_fuse_readdir, 1144 1065 .readlink = appfs_fuse_readlink, 1145 1066 .open = appfs_fuse_open, 1146 1067 .release = appfs_fuse_close, 1147 - .read = appfs_fuse_read 1068 + .read = appfs_fuse_read, 1069 + .write = appfs_fuse_write, 1070 + .mknod = appfs_fuse_mknod, 1071 + .create = appfs_fuse_create, 1072 + .truncate = appfs_fuse_truncate, 1073 + .unlink = appfs_fuse_unlink_rmdir, 1074 + .rmdir = appfs_fuse_unlink_rmdir, 1075 + .mkdir = appfs_fuse_mkdir, 1076 + .chmod = appfs_fuse_chmod, 1148 1077 }; 1149 1078 1079 +/* 1080 + * FUSE option parsing callback 1081 + */ 1082 +static int appfs_fuse_opt_cb(void *data, const char *arg, int key, struct fuse_args *outargs) { 1083 + static int seen_cachedir = 0; 1084 + 1085 + if (key == FUSE_OPT_KEY_NONOPT && seen_cachedir == 0) { 1086 + seen_cachedir = 1; 1087 + 1088 + appfs_cachedir = strdup(arg); 1089 + 1090 + return(0); 1091 + } 1092 + 1093 + return(1); 1094 +} 1095 + 1096 +/* 1097 + * Entry point into this program. 1098 + */ 1150 1099 int main(int argc, char **argv) { 1151 - const char *cachedir = APPFS_CACHEDIR; 1152 - char dbfilename[1024]; 1153 - int pthread_ret, snprintf_ret, sqlite_ret; 1100 + Tcl_Interp *test_interp; 1101 + char *test_interp_error; 1102 + struct fuse_args args = FUSE_ARGS_INIT(argc, argv); 1103 + int pthread_ret; 1154 1104 1155 - globalThread.cachedir = cachedir; 1156 - globalThread.boottime = time(NULL); 1157 - globalThread.platform = "linux-x86_64"; 1158 - globalThread.options.writable = 1; 1105 + /* 1106 + * Skip passed program name 1107 + */ 1108 + if (argc == 0 || argv == NULL) { 1109 + return(1); 1110 + } 1111 + argc--; 1112 + argv++; 1113 + 1114 + /* 1115 + * Set global variables, these should be configuration options. 1116 + */ 1117 + appfs_cachedir = APPFS_CACHEDIR; 1118 + 1119 + /* 1120 + * Set global variable for "boot time" to set a time on directories 1121 + * that we fake. 1122 + */ 1123 + appfs_boottime = time(NULL); 1124 + 1125 + /* 1126 + * Register "sha1" and "appfsd" package with libtcl so that any new 1127 + * interpreters created (which are done dynamically by FUSE) can have 1128 + * the appropriate configuration done automatically. 1129 + */ 1130 + Tcl_StaticPackage(NULL, "sha1", Sha1_Init, NULL); 1131 + Tcl_StaticPackage(NULL, "appfsd", Appfsd_Init, NULL); 1159 1132 1133 + /* 1134 + * Create a thread-specific-data (TSD) key for each thread to refer 1135 + * to its own Tcl interpreter. Tcl interpreters must be unique per 1136 + * thread and new threads are dynamically created by FUSE. 1137 + */ 1160 1138 pthread_ret = pthread_key_create(&interpKey, NULL); 1161 1139 if (pthread_ret != 0) { 1162 1140 fprintf(stderr, "Unable to create TSD key for Tcl. Aborting.\n"); 1163 1141 1164 1142 return(1); 1165 1143 } 1166 1144 1167 - snprintf_ret = snprintf(dbfilename, sizeof(dbfilename), "%s/%s", cachedir, "cache.db"); 1168 - if (snprintf_ret >= sizeof(dbfilename)) { 1169 - fprintf(stderr, "Unable to set database filename. Aborting.\n"); 1145 + /* 1146 + * Manually specify cache directory, without FUSE callback 1147 + * This option only works when not using FUSE, since we 1148 + * do not process it with FUSEs option processing. 1149 + */ 1150 + if (argc >= 2) { 1151 + if (strcmp(argv[0], "--cachedir") == 0) { 1152 + appfs_cachedir = strdup(argv[1]); 1153 + 1154 + argc -= 2; 1155 + argv += 2; 1156 + } 1157 + } 1158 + 1159 + /* 1160 + * SQLite3 mode, for running raw SQL against the cache database 1161 + */ 1162 + if (argc == 2 && strcmp(argv[0], "--sqlite3") == 0) { 1163 + return(appfs_sqlite3(argv[1])); 1164 + } 1165 + 1166 + /* 1167 + * Tcl mode, for running raw Tcl in the same environment AppFSd would 1168 + * run code. 1169 + */ 1170 + if (argc == 2 && strcmp(argv[0], "--tcl") == 0) { 1171 + return(appfs_tcl(argv[1])); 1172 + } 1173 + 1174 + /* 1175 + * Add FUSE arguments which we always supply 1176 + */ 1177 + fuse_opt_parse(&args, NULL, NULL, appfs_fuse_opt_cb); 1178 + fuse_opt_add_arg(&args, "-odefault_permissions,fsname=appfs,subtype=appfsd,use_ino,kernel_cache,entry_timeout=60,attr_timeout=3600,intr,big_writes"); 1170 1179 1171 - return(1); 1180 + if (getuid() == 0) { 1181 + fuse_opt_parse(&args, NULL, NULL, NULL); 1182 + fuse_opt_add_arg(&args, "-oallow_other"); 1172 1183 } 1173 1184 1174 - sqlite_ret = sqlite3_open(dbfilename, &globalThread.db); 1175 - if (sqlite_ret != SQLITE_OK) { 1176 - fprintf(stderr, "Unable to open database: %s\n", dbfilename); 1185 + /* 1186 + * Create a Tcl interpreter just to verify that things are in working 1187 + * order before we become a daemon. 1188 + */ 1189 + test_interp = appfs_create_TclInterp(&test_interp_error); 1190 + if (test_interp == NULL) { 1191 + if (test_interp_error == NULL) { 1192 + test_interp_error = "Unknown error"; 1193 + } 1194 + 1195 + fprintf(stderr, "Unable to initialize Tcl interpreter for AppFSd:\n"); 1196 + fprintf(stderr, "%s", test_interp_error); 1177 1197 1178 1198 return(1); 1179 1199 } 1200 + Tcl_DeleteInterp(test_interp); 1180 1201 1181 - return(fuse_main(argc, argv, &appfs_oper, NULL)); 1202 + /* 1203 + * Enter the FUSE main loop -- this will process any arguments 1204 + * and start servicing requests. 1205 + */ 1206 + appfs_fuse_started = 1; 1207 + return(fuse_main(args.argc, args.argv, &appfs_operations, NULL)); 1182 1208 } 1183 1209
Modified appfsd.tcl from [86cb2b92c8] to [3ec5e46dc5].
1 1 #! /usr/bin/env tclsh 2 2 3 3 package require http 2.7 4 4 package require sqlite3 5 - 6 -if {[catch { 7 - package require sha1 8 -}]} { 9 - @@SHA1.TCL@@ 10 - package require sha1 11 -} 5 +package require sha1 6 +package require appfsd 7 +package require platform 12 8 13 9 namespace eval ::appfs { 14 10 variable cachedir "/tmp/appfs-cache" 15 11 variable ttl 3600 16 12 variable nttl 60 13 + 14 + # User-replacable function to convert a hostname/hash/method to an URL 15 + proc _construct_url {hostname hash method} { 16 + return "http://$hostname/appfs/$method/$hash" 17 + } 17 18 18 19 proc _hash_sep {hash {seps 4}} { 19 20 for {set idx 0} {$idx < $seps} {incr idx} { 20 21 append retval "[string range $hash [expr {$idx * 2}] [expr {($idx * 2) + 1}]]/" 21 22 } 22 23 append retval "[string range $hash [expr {$idx * 2}] end]" 23 24 ................................................................................ 34 35 35 36 file mkdir [file dirname $file] 36 37 37 38 if {[file exists $file]} { 38 39 return $file 39 40 } 40 41 41 - set tmpfile "${file}.[expr {rand()}]" 42 + set tmpfile "${file}.[expr {rand()}][clock clicks]" 42 43 43 44 set fd [open $tmpfile "w"] 44 45 fconfigure $fd -translation binary 45 46 46 47 catch { 47 48 set token [::http::geturl $url -channel $fd -binary true] 48 49 } ................................................................................ 82 83 if {![regexp {^[0-9a-f]*$} $value]} { 83 84 return false 84 85 } 85 86 86 87 return true 87 88 } 88 89 89 - proc _db {args} { 90 - return [uplevel 1 [list ::appfs::db {*}$args]] 91 - } 92 - 93 90 proc _normalizeOS {os} { 94 91 set os [string tolower [string trim $os]] 95 92 96 93 switch -- $os { 97 94 "linux" - "freebsd" - "openbsd" - "netbsd" { 98 95 return $os 99 96 } ................................................................................ 126 123 return -code error "Unable to normalize CPU: $cpu" 127 124 } 128 125 129 126 proc init {} { 130 127 if {[info exists ::appfs::init_called]} { 131 128 return 132 129 } 130 + 131 + # Force [parray] to be loaded 132 + catch { 133 + parray does_not_exist 134 + } 133 135 134 136 set ::appfs::init_called 1 137 + 138 + # Load configuration file 139 + set config_file [file join $::appfs::cachedir config] 140 + if {[file exists $config_file]} { 141 + source $config_file 142 + } 135 143 136 144 if {![info exists ::appfs::db]} { 137 145 file mkdir $::appfs::cachedir 138 146 139 147 sqlite3 ::appfs::db [file join $::appfs::cachedir cache.db] 140 148 } 141 149 142 150 # Create tables 143 - _db eval {CREATE TABLE IF NOT EXISTS sites(hostname PRIMARY KEY, lastUpdate, ttl);} 144 - _db eval {CREATE TABLE IF NOT EXISTS packages(hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest);} 145 - _db eval {CREATE TABLE IF NOT EXISTS files(package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory);} 151 + db eval {CREATE TABLE IF NOT EXISTS sites(hostname PRIMARY KEY, lastUpdate, ttl);} 152 + db eval {CREATE TABLE IF NOT EXISTS packages(hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest);} 153 + db eval {CREATE TABLE IF NOT EXISTS files(package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory);} 146 154 147 155 # Create indexes 148 - _db eval {CREATE INDEX IF NOT EXISTS sites_index ON sites (hostname);} 149 - _db eval {CREATE INDEX IF NOT EXISTS packages_index ON packages (hostname, package, version, os, cpuArch);} 150 - _db eval {CREATE INDEX IF NOT EXISTS files_index ON files (package_sha1, file_name, file_directory);} 156 + db eval {CREATE INDEX IF NOT EXISTS sites_index ON sites (hostname);} 157 + db eval {CREATE INDEX IF NOT EXISTS packages_index ON packages (hostname, package, version, os, cpuArch);} 158 + db eval {CREATE INDEX IF NOT EXISTS files_index ON files (package_sha1, file_name, file_directory);} 151 159 } 152 160 153 161 proc download {hostname hash {method sha1}} { 154 - set url "http://$hostname/appfs/$method/$hash" 162 + set url [_construct_url $hostname $hash $method] 155 163 set file [_cachefile $url $hash] 156 164 157 165 if {![file exists $file]} { 158 166 return -code error "Unable to fetch (file does not exist: $file)" 159 167 } 160 168 161 169 return $file 162 170 } 163 171 164 172 proc getindex {hostname} { 165 173 set now [clock seconds] 166 174 167 - set lastUpdates [_db eval {SELECT lastUpdate, ttl FROM sites WHERE hostname = $hostname LIMIT 1;}] 175 + set lastUpdates [db eval {SELECT lastUpdate, ttl FROM sites WHERE hostname = $hostname LIMIT 1;}] 168 176 if {[llength $lastUpdates] == 0} { 169 177 set lastUpdate 0 170 178 set ttl 0 171 179 } else { 172 180 set lastUpdate [lindex $lastUpdates 0] 173 181 set ttl [lindex $lastUpdates 1] 174 182 } ................................................................................ 190 198 } 191 199 ::http::reset $token 192 200 ::http::cleanup $token 193 201 } 194 202 195 203 if {![info exists indexhash_data]} { 196 204 # Cache this result for 60 seconds 197 - _db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::nttl);} 205 + db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::nttl);} 198 206 199 207 return -code error "Unable to fetch $url" 200 208 } 201 209 202 210 set indexhash [lindex [split $indexhash_data ","] 0] 203 211 204 212 if {![_isHash $indexhash]} { ................................................................................ 240 248 if {![_isHash $pkgInfo(hash)]} { 241 249 continue 242 250 } 243 251 244 252 lappend curr_packages $pkgInfo(hash) 245 253 246 254 # Do not do any additional work if we already have this package 247 - set existing_packages [_db eval {SELECT package FROM packages WHERE hostname = $hostname AND sha1 = $pkgInfo(hash);}] 255 + set existing_packages [db eval {SELECT package FROM packages WHERE hostname = $hostname AND sha1 = $pkgInfo(hash);}] 248 256 if {[lsearch -exact $existing_packages $pkgInfo(package)] != -1} { 249 257 continue 250 258 } 251 259 252 260 if {$pkgInfo(isLatest)} { 253 - _db eval {UPDATE packages SET isLatest = 0 WHERE hostname = $hostname AND package = $pkgInfo($package) AND os = $pkgInfo($package) AND cpuArch = $pkgInfo(cpuArch);} 261 + db eval {UPDATE packages SET isLatest = 0 WHERE hostname = $hostname AND package = $pkgInfo($package) AND os = $pkgInfo($package) AND cpuArch = $pkgInfo(cpuArch);} 254 262 } 255 263 256 - _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);} 264 + 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);} 257 265 } 258 266 259 267 # Look for packages that have been deleted 260 - set found_packages [_db eval {SELECT sha1 FROM packages WHERE hostname = $hostname;}] 268 + set found_packages [db eval {SELECT sha1 FROM packages WHERE hostname = $hostname;}] 261 269 foreach package $found_packages { 262 270 set found_packages_arr($package) 1 263 271 } 264 272 265 273 foreach package $curr_packages { 266 274 unset -nocomplain found_packages_arr($package) 267 275 } 268 276 269 277 foreach package [array names found_packages_arr] { 270 - _db eval {DELETE FROM packages WHERE hostname = $hostname AND sha1 = $package;} 278 + db eval {DELETE FROM packages WHERE hostname = $hostname AND sha1 = $package;} 271 279 } 272 280 273 - _db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::ttl);} 281 + db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::ttl);} 274 282 275 283 return COMPLETE 276 284 } 277 285 278 286 proc getpkgmanifest {hostname package_sha1} { 279 - set haveManifests [_db eval {SELECT haveManifest FROM packages WHERE sha1 = $package_sha1 LIMIT 1;}] 287 + set haveManifests [db eval {SELECT haveManifest FROM packages WHERE sha1 = $package_sha1 LIMIT 1;}] 280 288 set haveManifest [lindex $haveManifests 0] 281 289 282 290 if {$haveManifest} { 283 291 return COMPLETE 284 292 } 285 293 286 294 if {![_isHash $package_sha1]} { ................................................................................ 288 296 } 289 297 290 298 set file [download $hostname $package_sha1] 291 299 set fd [open $file] 292 300 set pkgdata [read $fd] 293 301 close $fd 294 302 295 - _db transaction { 303 + db transaction { 296 304 foreach line [split $pkgdata "\n"] { 297 305 set line [string trim $line] 298 306 299 307 if {$line == ""} { 300 308 continue 301 309 } 302 310 ................................................................................ 322 330 } 323 331 324 332 set fileInfo(name) [join $work ","] 325 333 set fileInfo(name) [split [string trim $fileInfo(name) "/"] "/"] 326 334 set fileInfo(directory) [join [lrange $fileInfo(name) 0 end-1] "/"] 327 335 set fileInfo(name) [lindex $fileInfo(name) end] 328 336 329 - _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) );} 330 - _db eval {UPDATE packages SET haveManifest = 1 WHERE sha1 = $package_sha1;} 337 + 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) );} 338 + db eval {UPDATE packages SET haveManifest = 1 WHERE sha1 = $package_sha1;} 331 339 } 332 340 } 333 341 334 342 return COMPLETE 335 343 } 344 + 345 + proc _localpath {package hostname file} { 346 + set homedir [::appfsd::get_homedir] 347 + set dir [file join $homedir .appfs "./${package}@${hostname}" "./${file}"] 348 + return $dir 349 + } 350 + 351 + proc _whiteoutpath {package hostname file} { 352 + set homedir [::appfsd::get_homedir] 353 + set dir [file join $homedir .appfs "./${package}@${hostname}" ".APPFS.WHITEOUT" "./${file}.APPFS.WHITEOUT"] 354 + return $dir 355 + } 356 + 357 + proc _parsepath {path} { 358 + set path [string trim $path "/"] 359 + set path [split $path "/"] 360 + set pathlen [llength $path] 361 + 362 + array set retval [list _children sites _type toplevel] 363 + 364 + if {$pathlen > 0} { 365 + set retval(hostname) [lindex $path 0] 366 + set retval(_children) packages 367 + set retval(_type) sites 368 + 369 + if {$pathlen > 1} { 370 + set package [lindex $path 1] 371 + if {[string length $package] == "40" && [regexp {^[a-fA-F0-9]*$} $package]} { 372 + set retval(package_sha1) $package 373 + set retval(_children) files 374 + set retval(_type) files 375 + 376 + ::appfs::db eval {SELECT package, os, cpuArch, version FROM packages WHERE sha1 = $retval(package_sha1);} pkginfo {} 377 + set retval(package) $pkginfo(package) 378 + set retval(os) $pkginfo(os) 379 + set retval(cpu) $pkginfo(cpuArch) 380 + set retval(version) $pkginfo(version) 381 + 382 + if {$pathlen > 2} { 383 + set retval(file) [join [lrange $path 2 end] "/"] 384 + } else { 385 + set retval(file) "" 386 + } 387 + } else { 388 + set retval(package) $package 389 + set retval(_children) os-cpu 390 + set retval(_type) packages 391 + 392 + if {$pathlen > 2} { 393 + set os_cpu [lindex $path 2] 394 + set os_cpu [split $os_cpu "-"] 395 + 396 + set retval(os) [lindex $os_cpu 0] 397 + set retval(cpu) [lindex $os_cpu 1] 398 + set retval(_children) versions 399 + set retval(_type) os-cpu 400 + 401 + if {$pathlen > 3} { 402 + set retval(version) [lindex $path 3] 403 + set retval(_children) files 404 + set retval(_type) versions 405 + 406 + 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);}] 407 + if {$retval(package_sha1) == ""} { 408 + set retval(_children) dead 409 + return [array get retval] 410 + } 411 + 412 + if {$pathlen > 4} { 413 + set retval(_type) files 414 + set retval(file) [join [lrange $path 4 end] "/"] 415 + } else { 416 + set retval(_type) files 417 + set retval(file) "" 418 + } 419 + } 420 + } 421 + } 422 + } 423 + } 424 + 425 + return [array get retval] 426 + } 427 + 428 + proc getchildren {dir} { 429 + array set pathinfo [_parsepath $dir] 430 + 431 + switch -- $pathinfo(_children) { 432 + "sites" { 433 + return [::appfs::db eval {SELECT DISTINCT hostname FROM packages;}] 434 + } 435 + "packages" { 436 + catch { 437 + ::appfs::getindex $pathinfo(hostname) 438 + } 439 + 440 + return [::appfs::db eval {SELECT DISTINCT package FROM packages WHERE hostname = $pathinfo(hostname);}] 441 + } 442 + "os-cpu" { 443 + set retval [::appfs::db eval {SELECT DISTINCT os || "-" || cpuArch FROM packages WHERE hostname = $pathinfo(hostname) AND package = $pathinfo(package);}] 444 + 445 + lappend retval "platform" 446 + 447 + return $retval 448 + } 449 + "versions" { 450 + set retval [::appfs::db eval { 451 + SELECT DISTINCT version FROM packages WHERE hostname = $pathinfo(hostname) AND package = $pathinfo(package) AND os = $pathinfo(os) AND cpuArch = $pathinfo(cpu); 452 + }] 453 + 454 + lappend retval "latest" 455 + 456 + return $retval 457 + } 458 + "files" { 459 + catch { 460 + ::appfs::getpkgmanifest $pathinfo(hostname) $pathinfo(package_sha1) 461 + } 462 + 463 + set retval [::appfs::db eval {SELECT DISTINCT file_name FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_directory = $pathinfo(file);}] 464 + 465 + if {[info exists pathinfo(package)] && [info exists pathinfo(hostname)] && [info exists pathinfo(file)]} { 466 + set dir [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] 467 + set whiteoutdir [string range [_whiteoutpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] 0 end-15] 468 + 469 + foreach file [glob -nocomplain -tails -directory $whiteoutdir {{.,}*.APPFS.WHITEOUT}] { 470 + set remove [string range $file 0 end-15] 471 + set idx [lsearch -exact $retval $remove] 472 + if {$idx != -1} { 473 + set retval [lreplace $retval $idx $idx] 474 + } 475 + } 476 + 477 + foreach file [glob -nocomplain -tails -directory $dir -types {d f l} {{.,}*}] { 478 + if {$file == "." || $file == ".."} { 479 + continue 480 + } 481 + 482 + if {$file == ".APPFS.WHITEOUT"} { 483 + continue 484 + } 485 + 486 + if {[lsearch -exact $retval $file] != -1} { 487 + continue 488 + } 489 + 490 + lappend retval $file 491 + } 492 + } 493 + 494 + return $retval 495 + } 496 + } 497 + 498 + return -code error "Invalid or unacceptable path: $dir" 499 + } 500 + 501 + proc getattr {path} { 502 + array set pathinfo [_parsepath $path] 503 + array set retval [list] 504 + 505 + catch { 506 + ::appfs::getindex $pathinfo(hostname) 507 + ::appfs::getpkgmanifest $pathinfo(hostname) $pathinfo(package_sha1) 508 + } 509 + 510 + switch -- $pathinfo(_type) { 511 + "toplevel" { 512 + set retval(type) directory 513 + set retval(childcount) 2; 514 + } 515 + "sites" { 516 + set check [::appfs::db onecolumn {SELECT 1 FROM packages WHERE hostname = $pathinfo(hostname);}] 517 + if {$check == "1"} { 518 + set retval(type) directory 519 + set retval(childcount) 2; 520 + } 521 + } 522 + "packages" { 523 + set check [::appfs::db onecolumn {SELECT 1 FROM packages WHERE hostname = $pathinfo(hostname) AND package = $pathinfo(package);}] 524 + if {$check == "1"} { 525 + set retval(type) directory 526 + set retval(childcount) 2; 527 + } 528 + } 529 + "os-cpu" { 530 + if {$pathinfo(os) == "platform" && $pathinfo(cpu) == ""} { 531 + set retval(type) symlink 532 + set retval(source) [platform::generic] 533 + } else { 534 + set check [::appfs::db onecolumn { 535 + SELECT 1 FROM packages WHERE hostname = $pathinfo(hostname) AND package = $pathinfo(package) AND os = $pathinfo(os) AND cpuArch = $pathinfo(cpu); 536 + }] 537 + if {$check == "1"} { 538 + set retval(type) directory 539 + set retval(childcount) 2; 540 + } 541 + } 542 + } 543 + "versions" { 544 + if {$pathinfo(version) == "latest"} { 545 + set retval(type) symlink 546 + set retval(source) "1.0" 547 + } else { 548 + if {[info exists pathinfo(package_sha1)] && $pathinfo(package_sha1) != ""} { 549 + set retval(type) directory 550 + set retval(childcount) 2; 551 + } 552 + } 553 + } 554 + "files" { 555 + set retval(packaged) 1 556 + 557 + set localpath [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] 558 + set whiteoutpath [_whiteoutpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] 559 + 560 + set retval(localpath) $localpath 561 + set retval(whiteoutpath) $whiteoutpath 562 + 563 + if {[file exists $localpath]} { 564 + set retval(is_localfile) 1 565 + catch { 566 + file lstat $localpath localpathinfo 567 + set retval(time) $localpathinfo(mtime) 568 + 569 + switch -- $localpathinfo(type) { 570 + "directory" { 571 + set retval(type) "directory" 572 + set retval(childcount) 2 573 + } 574 + "file" { 575 + set retval(type) "file" 576 + set retval(size) $localpathinfo(size) 577 + if {[file executable $localpath]} { 578 + set retval(perms) "x" 579 + } else { 580 + set retval(perms) "" 581 + } 582 + } 583 + "link" { 584 + set retval(type) "symlink" 585 + set retval(source) [file readlink $localpath] 586 + } 587 + } 588 + } err 589 + } else { 590 + if {![file exists $whiteoutpath]} { 591 + set retval(is_remotefile) 1 592 + 593 + set work [split $pathinfo(file) "/"] 594 + set directory [join [lrange $work 0 end-1] "/"] 595 + set file [lindex $work end] 596 + 597 + if {$directory == "" && $file == ""} { 598 + array set retval [list type directory childcount 2] 599 + } 600 + 601 + ::appfs::db eval {SELECT type, time, source, size, perms FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_directory = $directory AND file_name = $file;} retval {} 602 + unset -nocomplain retval(*) 603 + } 604 + } 605 + 606 + } 607 + } 608 + 609 + if {![info exists retval(type)]} { 610 + return -code error "No such file or directory" 611 + } 612 + 613 + return [array get retval] 614 + } 615 + 616 + proc openpath {path mode} { 617 + array set pathinfo [_parsepath $path] 618 + 619 + if {$pathinfo(_type) != "files"} { 620 + return -code error "invalid type" 621 + } 622 + 623 + set localpath [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] 624 + 625 + if {$mode == "create"} { 626 + return $localpath 627 + } 628 + 629 + if {[file exists $localpath]} { 630 + return $localpath 631 + } 632 + 633 + set work [split $pathinfo(file) "/"] 634 + set directory [join [lrange $work 0 end-1] "/"] 635 + set file [lindex $work end] 636 + ::appfs::db eval {SELECT file_sha1, perms FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_name = $file AND file_directory = $directory;} pkgpathinfo {} 637 + 638 + if {$pkgpathinfo(file_sha1) == ""} { 639 + return -code error "No such file or directory" 640 + } 641 + 642 + set localcachefile [download $pathinfo(hostname) $pkgpathinfo(file_sha1)] 643 + 644 + if {$mode == "write"} { 645 + set tmplocalpath "${localpath}.[expr rand()][clock clicks]" 646 + 647 + set failed 0 648 + if {[catch { 649 + file mkdir [file dirname $localpath] 650 + file copy -force -- $localcachefile $tmplocalpath 651 + 652 + if {$pkgpathinfo(perms) == "x"} { 653 + file attributes $tmplocalpath -permissions +x 654 + } 655 + 656 + file rename -force -- $tmplocalpath $localpath 657 + } err]} { 658 + set failed 1 659 + } 660 + catch { 661 + file delete -force -- $tmplocalpath 662 + } 663 + 664 + if {$failed} { 665 + return -code error $err 666 + } 667 + 668 + return $localpath 669 + } 670 + 671 + return $localcachefile 672 + } 673 + 674 + proc localpath {path} { 675 + array set pathinfo [_parsepath $path] 676 + 677 + if {$pathinfo(_type) != "files"} { 678 + return -code error "invalid type" 679 + } 680 + 681 + set localpath [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)] 682 + 683 + return $localpath 684 + } 685 + 686 + proc exists {path} { 687 + catch { 688 + set info [getattr $path] 689 + } err 690 + 691 + if {![info exists info]} { 692 + if {$err == "No such file or directory"} { 693 + return [list] 694 + } else { 695 + return -code error $err 696 + } 697 + } 698 + 699 + return $info 700 + } 701 + 702 + proc prepare_to_create {path {must_not_exist 1}} { 703 + if {$must_not_exist} { 704 + if {[exists $path] != ""} { 705 + return -code error "File already exists" 706 + } 707 + } 708 + 709 + set filename [localpath $path] 710 + 711 + set dirname [file dirname $filename] 712 + 713 + file mkdir $dirname 714 + 715 + return $filename 716 + } 717 + 718 + proc unlinkpath {path} { 719 + array set pathattrs [exists $path] 720 + 721 + if {![info exists pathattrs(packaged)]} { 722 + return -code error "invalid type" 723 + } 724 + 725 + set localpath $pathattrs(localpath) 726 + 727 + set whiteout 0 728 + set isdirectory 0 729 + if {[info exists pathattrs(is_localfile)]} { 730 + if {[file isdirectory $localpath]} { 731 + set whiteout 1 732 + 733 + set isdirectory 1 734 + set children [getchildren $path] 735 + } 736 + file delete -force -- $localpath 737 + } elseif {[info exists pathattrs(is_remotefile)]} { 738 + if {$pathattrs(type) == "directory"} { 739 + set isdirectory 1 740 + set children [getchildren $path] 741 + } 742 + 743 + set whiteout 1 744 + } else { 745 + return -code error "Unknown if file is remote or local !?" 746 + } 747 + 748 + if {$isdirectory} { 749 + if {$children != [list]} { 750 + return -code error "Asked to delete non-empty directory" 751 + } 752 + } 753 + 754 + if {$whiteout} { 755 + set whiteoutfile $pathattrs(whiteoutpath) 756 + set whiteoutdir [file dirname $whiteoutfile] 757 + file mkdir $whiteoutdir 758 + close [open $whiteoutfile w] 759 + } 760 + } 336 761 }
Added sha1.c version [a59d293e66].
1 +/* 2 + SHA-1 in C 3 + By Steve Reid <steve@edmweb.com> 4 + 100% Public Domain 5 + 6 +Test Vectors (from FIPS PUB 180-1) 7 +"abc" 8 + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D 9 +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 10 + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 11 +A million repetitions of "a" 12 + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F 13 +*/ 14 + 15 +/* #define LITTLE_ENDIAN * This should be #define'd if true. */ 16 +/* #define SHA1HANDSOFF * Copies data before messing with it. */ 17 +#include <tcl.h> 18 +#include <sys/types.h> 19 +#include <sys/stat.h> 20 +#include <unistd.h> 21 +#include <stdint.h> 22 +#include <string.h> 23 +#include <fcntl.h> 24 +#include <stdio.h> 25 + 26 +#define SHA1HANDSOFF 1 27 + 28 +typedef struct { 29 + uint32_t state[5]; 30 + uint32_t count[2]; 31 + uint8_t buffer[64]; 32 +} SHA1_CTX; 33 + 34 +#ifndef __BIG_ENDIAN 35 +#define __BIG_ENDIAN 4321 36 +#endif 37 +#ifndef __LITTLE_ENDIAN 38 +#define __LITTLE_ENDIAN 1234 39 +#endif 40 + 41 +#ifndef __BYTE_ORDER 42 +#ifdef WORDS_BIGENDIAN 43 +#define __BYTE_ORDER __BIG_ENDIAN 44 +#else 45 +#define __BYTE_ORDER __LITTLE_ENDIAN 46 +#endif 47 +#endif 48 + 49 +#if __BYTE_ORDER == __BIG_ENDIAN 50 +#ifndef BIG_ENDIAN 51 +#define BIG_ENDIAN 1 52 +#endif 53 +#undef LITTLE_ENDIAN 54 +#else 55 +#ifndef LITTLE_ENDIAN 56 +#define LITTLE_ENDIAN 1 57 +#endif 58 +#undef BIG_ENDIAN 59 +#endif 60 + 61 +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 62 + 63 +/* blk0() and blk() perform the initial expand. */ 64 +/* I got the idea of expanding during the round function from SSLeay */ 65 +#ifdef LITTLE_ENDIAN 66 +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ 67 + |(rol(block->l[i],8)&0x00FF00FF)) 68 +#else 69 +#define blk0(i) block->l[i] 70 +#endif 71 +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 72 + ^block->l[(i+2)&15]^block->l[i&15],1)) 73 + 74 +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 75 +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 76 +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 77 +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 78 +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 79 +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 80 + 81 + 82 +/* Hash a single 512-bit block. This is the core of the algorithm. */ 83 + 84 +static void SHA1Transform(uint32_t state[5], uint8_t buffer[64]) { 85 + uint32_t a, b, c, d, e; 86 + typedef union { 87 + uint8_t c[64]; 88 + uint32_t l[16]; 89 + } CHAR64LONG16; 90 + CHAR64LONG16* block; 91 +#ifdef SHA1HANDSOFF 92 + uint8_t workspace[sizeof(*block)]; 93 + 94 + block = (CHAR64LONG16*)workspace; 95 + memcpy(block, buffer, sizeof(*block)); 96 +#else 97 + block = (CHAR64LONG16*)buffer; 98 +#endif 99 + 100 + /* Copy context->state[] to working vars */ 101 + a = state[0]; 102 + b = state[1]; 103 + c = state[2]; 104 + d = state[3]; 105 + e = state[4]; 106 + 107 + /* 4 rounds of 20 operations each. Loop unrolled. */ 108 + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); 109 + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); 110 + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); 111 + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); 112 + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); 113 + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); 114 + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); 115 + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); 116 + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); 117 + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); 118 + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); 119 + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); 120 + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); 121 + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); 122 + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); 123 + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); 124 + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); 125 + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); 126 + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); 127 + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); 128 + 129 + /* Add the working vars back into context.state[] */ 130 + state[0] += a; 131 + state[1] += b; 132 + state[2] += c; 133 + state[3] += d; 134 + state[4] += e; 135 + 136 + /* Wipe variables */ 137 + a = b = c = d = e = 0; 138 +} 139 + 140 + 141 +/* SHA1Init - Initialize new context */ 142 +static void SHA1Init(SHA1_CTX* context) { 143 + /* SHA1 initialization constants */ 144 + context->state[0] = 0x67452301; 145 + context->state[1] = 0xEFCDAB89; 146 + context->state[2] = 0x98BADCFE; 147 + context->state[3] = 0x10325476; 148 + context->state[4] = 0xC3D2E1F0; 149 + context->count[0] = 0; 150 + context->count[1] = 0; 151 +} 152 + 153 + 154 +/* Run your data through this. */ 155 +static void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) { 156 + unsigned int i, j; 157 + 158 + j = (context->count[0] >> 3) & 63; 159 + if ((context->count[0] += len << 3) < (len << 3)) { 160 + context->count[1]++; 161 + } 162 + 163 + context->count[1] += (len >> 29); 164 + 165 + if ((j + len) > 63) { 166 + memcpy(&context->buffer[j], data, (i = 64-j)); 167 + SHA1Transform(context->state, context->buffer); 168 + for ( ; i + 63 < len; i += 64) { 169 + SHA1Transform(context->state, &data[i]); 170 + } 171 + j = 0; 172 + } else { 173 + i = 0; 174 + } 175 + 176 + memcpy(&context->buffer[j], &data[i], len - i); 177 +} 178 + 179 + 180 +/* Add padding and return the message digest. */ 181 +static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { 182 + unsigned long i; 183 + unsigned char finalcount[8]; 184 + 185 + for (i = 0; i < 8; i++) { 186 + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ 187 + } 188 + 189 + SHA1Update(context, (unsigned char *) "\200", 1); 190 + 191 + while ((context->count[0] & 504) != 448) { 192 + SHA1Update(context, (unsigned char *)"\0", 1); 193 + } 194 + 195 + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ 196 + for (i = 0; i < 20; i++) { 197 + digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); 198 + } 199 + 200 + /* Wipe variables */ 201 + i = 0; 202 + 203 + memset(context->buffer, 0, 64); 204 + memset(context->state, 0, 20); 205 + memset(context->count, 0, 8); 206 + memset(&finalcount, 0, 8); 207 +#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ 208 + SHA1Transform(context->state, context->buffer); 209 +#endif 210 +} 211 + 212 +static Tcl_Obj* c_sha1__sha1_file(char* file) { 213 + SHA1_CTX ctx; 214 + unsigned char digest[20]; 215 + unsigned char buf[4096]; 216 + int fd; 217 + ssize_t read_ret; 218 + Tcl_Obj *ret; 219 + 220 + fd = open(file, O_RDONLY); 221 + if (fd < 0) { 222 + return(NULL); 223 + } 224 + 225 + SHA1Init(&ctx); 226 + 227 + while (1) { 228 + read_ret = read(fd, buf, sizeof(buf)); 229 + 230 + if (read_ret == 0) { 231 + break; 232 + } 233 + 234 + if (read_ret < 0) { 235 + close(fd); 236 + 237 + return(NULL); 238 + } 239 + 240 + SHA1Update(&ctx, buf, read_ret); 241 + } 242 + 243 + close(fd); 244 + 245 + SHA1Final(digest, &ctx); 246 + 247 + ret = Tcl_NewByteArrayObj(digest, sizeof(digest)); 248 + 249 + return(ret); 250 +} 251 + 252 +static int tcl_sha1__sha1_file(ClientData dummy, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[]) { 253 + char* _file; 254 + Tcl_Obj* rv; 255 + if (objc != 2) { 256 + Tcl_WrongNumArgs(ip, 1, objv, "file"); 257 + return TCL_ERROR; 258 + } 259 + _file = Tcl_GetString(objv[1]); 260 + 261 + rv = c_sha1__sha1_file(_file); 262 + if (rv == NULL) { 263 + return(TCL_ERROR); 264 + } 265 + Tcl_SetObjResult(ip, rv); 266 + return TCL_OK; 267 +} 268 + 269 +static Tcl_Obj* c_sha1__sha1_string(Tcl_Obj* str) { 270 + SHA1_CTX ctx; 271 + unsigned char digest[20]; 272 + unsigned char *buf; 273 + int buf_len; 274 + Tcl_Obj *ret; 275 + 276 + SHA1Init(&ctx); 277 + 278 + buf = Tcl_GetByteArrayFromObj(str, &buf_len); 279 + if (buf == NULL) { 280 + return(NULL); 281 + } 282 + 283 + SHA1Update(&ctx, buf, buf_len); 284 + 285 + SHA1Final(digest, &ctx); 286 + 287 + ret = Tcl_NewByteArrayObj(digest, sizeof(digest)); 288 + 289 + return(ret); 290 +} 291 + 292 +static int tcl_sha1__sha1_string(ClientData dummy, Tcl_Interp *ip, int objc, Tcl_Obj *CONST objv[]) { 293 + Tcl_Obj* _str; 294 + Tcl_Obj* rv; 295 + if (objc != 2) { 296 + Tcl_WrongNumArgs(ip, 1, objv, "str"); 297 + return TCL_ERROR; 298 + } 299 + _str = objv[1]; 300 + 301 + rv = c_sha1__sha1_string(_str); 302 + if (rv == NULL) { 303 + return(TCL_ERROR); 304 + } 305 + Tcl_SetObjResult(ip, rv); 306 + return TCL_OK; 307 +} 308 + 309 +int Sha1_Init(Tcl_Interp *interp) { 310 +#ifdef USE_TCL_STUBS 311 + if (Tcl_InitStubs(interp, TCL_VERSION, 0) == 0L) { 312 + return TCL_ERROR; 313 + } 314 +#endif 315 + Tcl_CreateObjCommand(interp, "sha1::_sha1_file", tcl_sha1__sha1_file, NULL, NULL); 316 + Tcl_CreateObjCommand(interp, "sha1::_sha1_string", tcl_sha1__sha1_string, NULL, NULL); 317 + Tcl_Eval(interp, 318 +#include "sha1.tcl.h" 319 + ); 320 + Tcl_PkgProvide(interp, "sha1", "1.0"); 321 + return(TCL_OK); 322 +}
Modified sha1.tcl from [a8b3b2afbe] to [235aac1913].
1 -# sha1.tcl - 1 +#! /usr/bin/env tclsh 2 + 3 +proc sha1::sha1 args { 4 + set outputmode "hex" 5 + 6 + if {[lindex $args 0] == "-hex"} { 7 + set outputmode "hex" 8 + 9 + set args [lrange $args 1 end] 10 + } elseif {[lindex $args 0] == "-bin"} { 11 + set outputmode "binary" 12 + 13 + set args [lrange $args 1 end] 14 + } 2 15 3 -# @@ Meta Begin 4 -# Package sha1 2.0.3 5 -# Meta platform tcl 6 -# Meta rsk::build::date 2011-03-30 7 -# Meta description Part of the Tclib sha1 module 8 -# Meta require {Tcl 8.2} 9 -# @@ Meta End 16 + if {[llength $args] == 2} { 17 + set mode [lindex $args 0] 18 + } elseif {[llength $args] == 1} { 19 + set mode "-string" 20 + } else { 21 + return -code error "wrong # args: sha1::sha1 ?-bin|-hex? ?-channel channel|-file file|string?" 22 + } 10 23 11 -# 12 -# Copyright (C) 2001 Don Libes <libes@nist.gov> 13 -# Copyright (C) 2003 Pat Thoyts <patthoyts@users.sourceforge.net> 14 -# 15 -# SHA1 defined by FIPS 180-1, "The SHA1 Message-Digest Algorithm" 16 -# HMAC defined by RFC 2104, "Keyed-Hashing for Message Authentication" 17 -# 18 -# This is an implementation of SHA1 based upon the example code given in 19 -# FIPS 180-1 and upon the tcllib MD4 implementation and taking some ideas 20 -# and methods from the earlier tcllib sha1 version by Don Libes. 21 -# 22 -# This implementation permits incremental updating of the hash and 23 -# provides support for external compiled implementations either using 24 -# critcl (sha1c) or Trf. 25 -# 26 -# ref: http://www.itl.nist.gov/fipspubs/fip180-1.htm 27 -# 28 -# ------------------------------------------------------------------------- 29 -# See the file "license.terms" for information on usage and redistribution 30 -# of this file, and for a DISCLAIMER OF ALL WARRANTIES. 31 -# ------------------------------------------------------------------------- 32 -# 33 -# $Id: sha1.tcl,v 1.22 2009/05/07 00:35:10 patthoyts Exp $ 24 + switch -- $mode { 25 + "-channel" { 26 + return -code error "Not implemented" 27 + } 28 + "-file" { 29 + set output [_sha1_file [lindex $args end]] 30 + } 31 + "-string" { 32 + set output [_sha1_string [lindex $args end]] 33 + } 34 + default { 35 + return -code error "invalid mode: $mode, must be one of -channel or -file (or a plain string)" 36 + } 37 + } 34 38 35 -# @mdgen EXCLUDE: sha1c.tcl 36 - 37 -package require Tcl 8.2; # tcl minimum version 38 - 39 -namespace eval ::sha1 { 40 - variable version 2.0.3 41 - namespace export sha1 hmac SHA1Init SHA1Update SHA1Final 42 - variable uid 43 - if {![info exists uid]} { 44 - set uid 0 45 - } 46 -} 39 + if {$outputmode == "hex"} { 40 + binary scan $output H* output 41 + } 47 42 48 -proc ::sha1::SHA1Init {} { 49 - variable uid 50 - set token [namespace current]::[incr uid] 51 - upvar #0 $token state 52 - 53 - # FIPS 180-1: 7 - Initialize the hash state 54 - array set state \ 55 - [list \ 56 - A [expr {int(0x67452301)}] \ 57 - B [expr {int(0xEFCDAB89)}] \ 58 - C [expr {int(0x98BADCFE)}] \ 59 - D [expr {int(0x10325476)}] \ 60 - E [expr {int(0xC3D2E1F0)}] \ 61 - n 0 i "" ] 62 - return $token 63 -} 64 - 65 -# SHA1Update -- 66 -# 67 -# This is called to add more data into the hash. You may call this 68 -# as many times as you require. Note that passing in "ABC" is equivalent 69 -# to passing these letters in as separate calls -- hence this proc 70 -# permits hashing of chunked data 71 -# 72 -# If we have a C-based implementation available, then we will use 73 -# it here in preference to the pure-Tcl implementation. 74 -# 75 -proc ::sha1::SHA1Update {token data} { 76 - upvar #0 $token state 77 - 78 - # Update the state values 79 - incr state(n) [string length $data] 80 - append state(i) $data 81 - 82 - # Calculate the hash for any complete blocks 83 - set len [string length $state(i)] 84 - for {set n 0} {($n + 64) <= $len} {} { 85 - SHA1Transform $token [string range $state(i) $n [incr n 64]] 86 - } 87 - 88 - # Adjust the state for the blocks completed. 89 - set state(i) [string range $state(i) $n end] 90 - return 91 -} 92 - 93 -# SHA1Final -- 94 -# 95 -# This procedure is used to close the current hash and returns the 96 -# hash data. Once this procedure has been called the hash context 97 -# is freed and cannot be used again. 98 -# 99 -# Note that the output is 160 bits represented as binary data. 100 -# 101 -proc ::sha1::SHA1Final {token} { 102 - upvar #0 $token state 103 - 104 - # Padding 105 - # 106 - set len [string length $state(i)] 107 - set pad [expr {56 - ($len % 64)}] 108 - if {$len % 64 > 56} { 109 - incr pad 64 110 - } 111 - if {$pad == 0} { 112 - incr pad 64 113 - } 114 - append state(i) [binary format a$pad \x80] 115 - 116 - # Append length in bits as big-endian wide int. 117 - set dlen [expr {8 * $state(n)}] 118 - append state(i) [binary format II 0 $dlen] 119 - 120 - # Calculate the hash for the remaining block. 121 - set len [string length $state(i)] 122 - for {set n 0} {($n + 64) <= $len} {} { 123 - SHA1Transform $token [string range $state(i) $n [incr n 64]] 124 - } 125 - 126 - # Output 127 - set r [bytes $state(A)][bytes $state(B)][bytes $state(C)][bytes $state(D)][bytes $state(E)] 128 - unset state 129 - return $r 130 -} 131 - 132 -# ------------------------------------------------------------------------- 133 -# HMAC Hashed Message Authentication (RFC 2104) 134 -# 135 -# hmac = H(K xor opad, H(K xor ipad, text)) 136 -# 137 - 138 -# HMACInit -- 139 -# 140 -# This is equivalent to the SHA1Init procedure except that a key is 141 -# added into the algorithm 142 -# 143 -proc ::sha1::HMACInit {K} { 144 - 145 - # Key K is adjusted to be 64 bytes long. If K is larger, then use 146 - # the SHA1 digest of K and pad this instead. 147 - set len [string length $K] 148 - if {$len > 64} { 149 - set tok [SHA1Init] 150 - SHA1Update $tok $K 151 - set K [SHA1Final $tok] 152 - set len [string length $K] 153 - } 154 - set pad [expr {64 - $len}] 155 - append K [string repeat \0 $pad] 156 - 157 - # Cacluate the padding buffers. 158 - set Ki {} 159 - set Ko {} 160 - binary scan $K i16 Ks 161 - foreach k $Ks { 162 - append Ki [binary format i [expr {$k ^ 0x36363636}]] 163 - append Ko [binary format i [expr {$k ^ 0x5c5c5c5c}]] 164 - } 165 - 166 - set tok [SHA1Init] 167 - SHA1Update $tok $Ki; # initialize with the inner pad 168 - 169 - # preserve the Ko value for the final stage. 170 - # FRINK: nocheck 171 - set [subst $tok](Ko) $Ko 172 - 173 - return $tok 174 -} 175 - 176 -# HMACUpdate -- 177 -# 178 -# Identical to calling SHA1Update 179 -# 180 -proc ::sha1::HMACUpdate {token data} { 181 - SHA1Update $token $data 182 - return 183 -} 184 - 185 -# HMACFinal -- 186 -# 187 -# This is equivalent to the SHA1Final procedure. The hash context is 188 -# closed and the binary representation of the hash result is returned. 189 -# 190 -proc ::sha1::HMACFinal {token} { 191 - upvar #0 $token state 192 - 193 - set tok [SHA1Init]; # init the outer hashing function 194 - SHA1Update $tok $state(Ko); # prepare with the outer pad. 195 - SHA1Update $tok [SHA1Final $token]; # hash the inner result 196 - return [SHA1Final $tok] 197 -} 198 - 199 -# ------------------------------------------------------------------------- 200 -# Description: 201 -# This is the core SHA1 algorithm. It is a lot like the MD4 algorithm but 202 -# includes an extra round and a set of constant modifiers throughout. 203 -# 204 -set ::sha1::SHA1Transform_body { 205 - upvar #0 $token state 206 - 207 - # FIPS 180-1: 7a: Process Message in 16-Word Blocks 208 - binary scan $msg I* blocks 209 - set blockLen [llength $blocks] 210 - for {set i 0} {$i < $blockLen} {incr i 16} { 211 - set W [lrange $blocks $i [expr {$i+15}]] 212 - 213 - # FIPS 180-1: 7b: Expand the input into 80 words 214 - # For t = 16 to 79 215 - # let Wt = (Wt-3 ^ Wt-8 ^ Wt-14 ^ Wt-16) <<< 1 216 - set t3 12 217 - set t8 7 218 - set t14 1 219 - set t16 -1 220 - for {set t 16} {$t < 80} {incr t} { 221 - set x [expr {[lindex $W [incr t3]] ^ [lindex $W [incr t8]] ^ \ 222 - [lindex $W [incr t14]] ^ [lindex $W [incr t16]]}] 223 - lappend W [expr {int(($x << 1) | (($x >> 31) & 1))}] 224 - } 225 - 226 - # FIPS 180-1: 7c: Copy hash state. 227 - set A $state(A) 228 - set B $state(B) 229 - set C $state(C) 230 - set D $state(D) 231 - set E $state(E) 232 - 233 - # FIPS 180-1: 7d: Do permutation rounds 234 - # For t = 0 to 79 do 235 - # TEMP = (A<<<5) + ft(B,C,D) + E + Wt + Kt; 236 - # E = D; D = C; C = S30(B); B = A; A = TEMP; 237 - 238 - # Round 1: ft(B,C,D) = (B & C) | (~B & D) ( 0 <= t <= 19) 239 - for {set t 0} {$t < 20} {incr t} { 240 - set TEMP [F1 $A $B $C $D $E [lindex $W $t]] 241 - set E $D 242 - set D $C 243 - set C [rotl32 $B 30] 244 - set B $A 245 - set A $TEMP 246 - } 247 - 248 - # Round 2: ft(B,C,D) = (B ^ C ^ D) ( 20 <= t <= 39) 249 - for {} {$t < 40} {incr t} { 250 - set TEMP [F2 $A $B $C $D $E [lindex $W $t]] 251 - set E $D 252 - set D $C 253 - set C [rotl32 $B 30] 254 - set B $A 255 - set A $TEMP 256 - } 257 - 258 - # Round 3: ft(B,C,D) = ((B & C) | (B & D) | (C & D)) ( 40 <= t <= 59) 259 - for {} {$t < 60} {incr t} { 260 - set TEMP [F3 $A $B $C $D $E [lindex $W $t]] 261 - set E $D 262 - set D $C 263 - set C [rotl32 $B 30] 264 - set B $A 265 - set A $TEMP 266 - } 267 - 268 - # Round 4: ft(B,C,D) = (B ^ C ^ D) ( 60 <= t <= 79) 269 - for {} {$t < 80} {incr t} { 270 - set TEMP [F4 $A $B $C $D $E [lindex $W $t]] 271 - set E $D 272 - set D $C 273 - set C [rotl32 $B 30] 274 - set B $A 275 - set A $TEMP 276 - } 277 - 278 - # Then perform the following additions. (That is, increment each 279 - # of the four registers by the value it had before this block 280 - # was started.) 281 - incr state(A) $A 282 - incr state(B) $B 283 - incr state(C) $C 284 - incr state(D) $D 285 - incr state(E) $E 286 - } 287 - 288 - return 289 -} 290 - 291 -proc ::sha1::F1 {A B C D E W} { 292 - expr {(((($A << 5) & 0xffffffff) | (($A >> 27) & 0x1f)) \ 293 - + ($D ^ ($B & ($C ^ $D))) + $E + $W + 0x5a827999) & 0xffffffff} 294 -} 295 - 296 -proc ::sha1::F2 {A B C D E W} { 297 - expr {(((($A << 5) & 0xffffffff) | (($A >> 27) & 0x1f)) \ 298 - + ($B ^ $C ^ $D) + $E + $W + 0x6ed9eba1) & 0xffffffff} 299 -} 300 - 301 -proc ::sha1::F3 {A B C D E W} { 302 - expr {(((($A << 5) & 0xffffffff)| (($A >> 27) & 0x1f)) \ 303 - + (($B & $C) | ($D & ($B | $C))) + $E + $W + 0x8f1bbcdc) & 0xffffffff} 304 -} 305 - 306 -proc ::sha1::F4 {A B C D E W} { 307 - expr {(((($A << 5) & 0xffffffff)| (($A >> 27) & 0x1f)) \ 308 - + ($B ^ $C ^ $D) + $E + $W + 0xca62c1d6) & 0xffffffff} 309 -} 310 - 311 -proc ::sha1::rotl32 {v n} { 312 - return [expr {((($v << $n) \ 313 - | (($v >> (32 - $n)) \ 314 - & (0x7FFFFFFF >> (31 - $n))))) \ 315 - & 0xFFFFFFFF}] 316 -} 317 - 318 - 319 -# ------------------------------------------------------------------------- 320 -# 321 -# In order to get this code to go as fast as possible while leaving 322 -# the main code readable we can substitute the above function bodies 323 -# into the transform procedure. This inlines the code for us an avoids 324 -# a procedure call overhead within the loops. 325 -# 326 -# We can do some minor tweaking to improve speed on Tcl < 8.5 where we 327 -# know our arithmetic is limited to 64 bits. On > 8.5 we may have 328 -# unconstrained integer arithmetic and must avoid letting it run away. 329 -# 330 - 331 -regsub -all -line \ 332 - {\[F1 \$A \$B \$C \$D \$E (\[.*?\])\]} \ 333 - $::sha1::SHA1Transform_body \ 334 - {[expr {(rotl32($A,5) + ($D ^ ($B \& ($C ^ $D))) + $E + \1 + 0x5a827999) \& 0xffffffff}]} \ 335 - ::sha1::SHA1Transform_body_tmp 336 - 337 -regsub -all -line \ 338 - {\[F2 \$A \$B \$C \$D \$E (\[.*?\])\]} \ 339 - $::sha1::SHA1Transform_body_tmp \ 340 - {[expr {(rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0x6ed9eba1) \& 0xffffffff}]} \ 341 - ::sha1::SHA1Transform_body_tmp 342 - 343 -regsub -all -line \ 344 - {\[F3 \$A \$B \$C \$D \$E (\[.*?\])\]} \ 345 - $::sha1::SHA1Transform_body_tmp \ 346 - {[expr {(rotl32($A,5) + (($B \& $C) | ($D \& ($B | $C))) + $E + \1 + 0x8f1bbcdc) \& 0xffffffff}]} \ 347 - ::sha1::SHA1Transform_body_tmp 348 - 349 -regsub -all -line \ 350 - {\[F4 \$A \$B \$C \$D \$E (\[.*?\])\]} \ 351 - $::sha1::SHA1Transform_body_tmp \ 352 - {[expr {(rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0xca62c1d6) \& 0xffffffff}]} \ 353 - ::sha1::SHA1Transform_body_tmp 354 - 355 -regsub -all -line \ 356 - {rotl32\(\$A,5\)} \ 357 - $::sha1::SHA1Transform_body_tmp \ 358 - {((($A << 5) \& 0xffffffff) | (($A >> 27) \& 0x1f))} \ 359 - ::sha1::SHA1Transform_body_tmp 360 - 361 -regsub -all -line \ 362 - {\[rotl32 \$B 30\]} \ 363 - $::sha1::SHA1Transform_body_tmp \ 364 - {[expr {int(($B << 30) | (($B >> 2) \& 0x3fffffff))}]} \ 365 - ::sha1::SHA1Transform_body_tmp 366 -# 367 -# Version 2 avoids a few truncations to 32 bits in non-essential places. 368 -# 369 -regsub -all -line \ 370 - {\[F1 \$A \$B \$C \$D \$E (\[.*?\])\]} \ 371 - $::sha1::SHA1Transform_body \ 372 - {[expr {rotl32($A,5) + ($D ^ ($B \& ($C ^ $D))) + $E + \1 + 0x5a827999}]} \ 373 - ::sha1::SHA1Transform_body_tmp2 374 - 375 -regsub -all -line \ 376 - {\[F2 \$A \$B \$C \$D \$E (\[.*?\])\]} \ 377 - $::sha1::SHA1Transform_body_tmp2 \ 378 - {[expr {rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0x6ed9eba1}]} \ 379 - ::sha1::SHA1Transform_body_tmp2 380 - 381 -regsub -all -line \ 382 - {\[F3 \$A \$B \$C \$D \$E (\[.*?\])\]} \ 383 - $::sha1::SHA1Transform_body_tmp2 \ 384 - {[expr {rotl32($A,5) + (($B \& $C) | ($D \& ($B | $C))) + $E + \1 + 0x8f1bbcdc}]} \ 385 - ::sha1::SHA1Transform_body_tmp2 386 - 387 -regsub -all -line \ 388 - {\[F4 \$A \$B \$C \$D \$E (\[.*?\])\]} \ 389 - $::sha1::SHA1Transform_body_tmp2 \ 390 - {[expr {rotl32($A,5) + ($B ^ $C ^ $D) + $E + \1 + 0xca62c1d6}]} \ 391 - ::sha1::SHA1Transform_body_tmp2 392 - 393 -regsub -all -line \ 394 - {rotl32\(\$A,5\)} \ 395 - $::sha1::SHA1Transform_body_tmp2 \ 396 - {(($A << 5) | (($A >> 27) \& 0x1f))} \ 397 - ::sha1::SHA1Transform_body_tmp2 398 - 399 -regsub -all -line \ 400 - {\[rotl32 \$B 30\]} \ 401 - $::sha1::SHA1Transform_body_tmp2 \ 402 - {[expr {($B << 30) | (($B >> 2) \& 0x3fffffff)}]} \ 403 - ::sha1::SHA1Transform_body_tmp2 404 - 405 -if {[package vsatisfies [package provide Tcl] 8.5]} { 406 - proc ::sha1::SHA1Transform {token msg} $::sha1::SHA1Transform_body_tmp 407 -} else { 408 - proc ::sha1::SHA1Transform {token msg} $::sha1::SHA1Transform_body_tmp2 409 -} 410 - 411 -unset ::sha1::SHA1Transform_body 412 -unset ::sha1::SHA1Transform_body_tmp 413 -unset ::sha1::SHA1Transform_body_tmp2 414 - 415 -# ------------------------------------------------------------------------- 416 - 417 -proc ::sha1::byte {n v} {expr {((0xFF << (8 * $n)) & $v) >> (8 * $n)}} 418 -proc ::sha1::bytes {v} { 419 - #format %c%c%c%c [byte 0 $v] [byte 1 $v] [byte 2 $v] [byte 3 $v] 420 - format %c%c%c%c \ 421 - [expr {((0xFF000000 & $v) >> 24) & 0xFF}] \ 422 - [expr {(0xFF0000 & $v) >> 16}] \ 423 - [expr {(0xFF00 & $v) >> 8}] \ 424 - [expr {0xFF & $v}] 425 -} 426 - 427 -# ------------------------------------------------------------------------- 428 - 429 -proc ::sha1::Hex {data} { 430 - binary scan $data H* result 431 - return $result 432 -} 433 - 434 -# ------------------------------------------------------------------------- 435 - 436 -# Description: 437 -# Pop the nth element off a list. Used in options processing. 438 -# 439 -proc ::sha1::Pop {varname {nth 0}} { 440 - upvar $varname args 441 - set r [lindex $args $nth] 442 - set args [lreplace $args $nth $nth] 443 - return $r 444 -} 445 - 446 -# ------------------------------------------------------------------------- 447 - 448 -# fileevent handler for chunked file hashing. 449 -# 450 -proc ::sha1::Chunk {token channel {chunksize 4096}} { 451 - upvar #0 $token state 452 - 453 - if {[eof $channel]} { 454 - fileevent $channel readable {} 455 - set state(reading) 0 456 - } 457 - 458 - SHA1Update $token [read $channel $chunksize] 459 -} 460 - 461 -# ------------------------------------------------------------------------- 462 - 463 -proc ::sha1::sha1 {args} { 464 - array set opts {-hex 0 -filename {} -channel {} -chunksize 4096} 465 - if {[llength $args] == 1} { 466 - set opts(-hex) 1 467 - } else { 468 - while {[string match -* [set option [lindex $args 0]]]} { 469 - switch -glob -- $option { 470 - -hex { set opts(-hex) 1 } 471 - -bin { set opts(-hex) 0 } 472 - -file* { set opts(-filename) [Pop args 1] } 473 - -channel { set opts(-channel) [Pop args 1] } 474 - -chunksize { set opts(-chunksize) [Pop args 1] } 475 - default { 476 - if {[llength $args] == 1} { break } 477 - if {[string compare $option "--"] == 0} { Pop args; break } 478 - set err [join [lsort [concat -bin [array names opts]]] ", "] 479 - return -code error "bad option $option:\ 480 - must be one of $err" 481 - } 482 - } 483 - Pop args 484 - } 485 - } 486 - 487 - if {$opts(-filename) != {}} { 488 - set opts(-channel) [open $opts(-filename) r] 489 - fconfigure $opts(-channel) -translation binary 490 - } 491 - 492 - if {$opts(-channel) == {}} { 493 - 494 - if {[llength $args] != 1} { 495 - return -code error "wrong # args:\ 496 - should be \"sha1 ?-hex? -filename file | string\"" 497 - } 498 - set tok [SHA1Init] 499 - SHA1Update $tok [lindex $args 0] 500 - set r [SHA1Final $tok] 501 - 502 - } else { 503 - 504 - set tok [SHA1Init] 505 - # FRINK: nocheck 506 - set [subst $tok](reading) 1 507 - fileevent $opts(-channel) readable \ 508 - [list [namespace origin Chunk] \ 509 - $tok $opts(-channel) $opts(-chunksize)] 510 - # FRINK: nocheck 511 - vwait [subst $tok](reading) 512 - set r [SHA1Final $tok] 513 - 514 - # If we opened the channel - we should close it too. 515 - if {$opts(-filename) != {}} { 516 - close $opts(-channel) 517 - } 518 - } 519 - 520 - if {$opts(-hex)} { 521 - set r [Hex $r] 522 - } 523 - return $r 524 -} 525 - 526 -# ------------------------------------------------------------------------- 527 - 528 -proc ::sha1::hmac {args} { 529 - array set opts {-hex 1 -filename {} -channel {} -chunksize 4096} 530 - if {[llength $args] != 2} { 531 - while {[string match -* [set option [lindex $args 0]]]} { 532 - switch -glob -- $option { 533 - -key { set opts(-key) [Pop args 1] } 534 - -hex { set opts(-hex) 1 } 535 - -bin { set opts(-hex) 0 } 536 - -file* { set opts(-filename) [Pop args 1] } 537 - -channel { set opts(-channel) [Pop args 1] } 538 - -chunksize { set opts(-chunksize) [Pop args 1] } 539 - default { 540 - if {[llength $args] == 1} { break } 541 - if {[string compare $option "--"] == 0} { Pop args; break } 542 - set err [join [lsort [array names opts]] ", "] 543 - return -code error "bad option $option:\ 544 - must be one of $err" 545 - } 546 - } 547 - Pop args 548 - } 549 - } 550 - 551 - if {[llength $args] == 2} { 552 - set opts(-key) [Pop args] 553 - } 554 - 555 - if {![info exists opts(-key)]} { 556 - return -code error "wrong # args:\ 557 - should be \"hmac ?-hex? -key key -filename file | string\"" 558 - } 559 - 560 - if {$opts(-filename) != {}} { 561 - set opts(-channel) [open $opts(-filename) r] 562 - fconfigure $opts(-channel) -translation binary 563 - } 564 - 565 - if {$opts(-channel) == {}} { 566 - 567 - if {[llength $args] != 1} { 568 - return -code error "wrong # args:\ 569 - should be \"hmac ?-hex? -key key -filename file | string\"" 570 - } 571 - set tok [HMACInit $opts(-key)] 572 - HMACUpdate $tok [lindex $args 0] 573 - set r [HMACFinal $tok] 574 - 575 - } else { 576 - 577 - set tok [HMACInit $opts(-key)] 578 - # FRINK: nocheck 579 - set [subst $tok](reading) 1 580 - fileevent $opts(-channel) readable \ 581 - [list [namespace origin Chunk] \ 582 - $tok $opts(-channel) $opts(-chunksize)] 583 - # FRINK: nocheck 584 - vwait [subst $tok](reading) 585 - set r [HMACFinal $tok] 586 - 587 - # If we opened the channel - we should close it too. 588 - if {$opts(-filename) != {}} { 589 - close $opts(-channel) 590 - } 591 - } 592 - 593 - if {$opts(-hex)} { 594 - set r [Hex $r] 595 - } 596 - return $r 597 -} 598 - 599 -# ------------------------------------------------------------------------- 600 - 601 -package provide sha1 $::sha1::version 602 - 603 -# ------------------------------------------------------------------------- 604 -# Local Variables: 605 -# mode: tcl 606 -# indent-tabs-mode: nil 607 -# End: 43 + return $output 44 +}