Changes On Branch a6cb1222223daec2

Changes In Branch tcl-ops Through [a6cb122222] Excluding Merge-Ins

This is equivalent to a diff from 98449bcf3e to a6cb122222

2014-11-10
05:57
Moved to tcl-ops branch check-in: 0effed3239 user: rkeene tags: trunk
2014-11-07
07:20
Converted global variables to not be part of a struct check-in: f277407cbc user: rkeene tags: tcl-ops
07:17
Updated Makefile check-in: a6cb122222 user: rkeene tags: tcl-ops
07:15
Removed extraneous debugging check-in: ed1bf1669b 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 [5e87139775].

    20     20   TCLCONFIG_SH_PATH = $(shell echo 'puts [::tcl::pkgconfig get libdir,install]' | tclsh)/tclConfig.sh
    21     21   endif
    22     22   TCL_CFLAGS = $(shell . $(TCLCONFIG_SH_PATH); echo "$${TCL_INCLUDE_SPEC}")
    23     23   TCL_LIBS = $(shell . $(TCLCONFIG_SH_PATH); echo "$${TCL_LIB_SPEC}")
    24     24   
    25     25   all: appfsd
    26     26   
    27         -appfsd: appfsd.o
    28         -	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o appfsd appfsd.o $(LIBS)
           27  +appfsd: appfsd.o sha1.o
           28  +	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o appfsd appfsd.o sha1.o $(LIBS)
    29     29   
    30     30   appfsd.o: appfsd.c appfsd.tcl.h
    31     31   	$(CC) $(CPPFLAGS) $(CFLAGS) -o appfsd.o -c appfsd.c
    32     32   
    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
           33  +sha1.o: sha1.c sha1.tcl.h
           34  +	$(CC) $(CPPFLAGS) $(CFLAGS) -o sha1.o -c sha1.c
           35  +
           36  +%.tcl.h: %.tcl
           37  +	sed 's@[\\"]@\\&@g;s@^@   "@;s@$$@\\n"@' $^ > $@.new
           38  +	mv $@.new $@
    36     39   
    37         -install: appfsd
           40  +install: appfsd appfs-cache appfs-mkfs
    38     41   	if [ ! -d '$(DESTDIR)$(sbindir)' ]; then mkdir -p '$(DESTDIR)$(sbindir)'; chmod 755 '$(DESTDIR)$(sbindir)'; fi
           42  +	if [ ! -d '$(DESTDIR)$(bindir)' ]; then mkdir -p '$(DESTDIR)$(bindir)'; chmod 755 '$(DESTDIR)$(bindir)'; fi
    39     43   	cp appfsd '$(DESTDIR)$(sbindir)/'
           44  +	cp appfs-cache '$(DESTDIR)$(sbindir)/'
           45  +	cp appfs-mkfs '$(DESTDIR)$(bindir)/'
    40     46   
    41     47   clean:
    42     48   	rm -f appfsd appfsd.o
    43     49   	rm -f appfsd.tcl.h
           50  +	rm -f sha1.o sha1.tcl.h
    44     51   
    45     52   distclean: clean
    46     53   
    47     54   .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 [2188b35dbc].

     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   
           40  +/*
           41  + * Data for a given thread (most likely these will become just globable variables soon.)
           42  + */
    29     43   struct appfs_thread_data {
    30         -	sqlite3 *db;
    31     44   	const char *cachedir;
    32     45   	time_t boottime;
    33         -	const char *platform;
    34     46   	struct {
    35     47   		int writable;
    36     48   	} options;
    37     49   };
    38     50   
           51  +/* The global thread data (likely to go away) */
    39     52   struct appfs_thread_data globalThread;
    40     53   
           54  +/*
           55  + * AppFS Path Type:  Describes the type of path a given file is
           56  + */
    41     57   typedef enum {
    42     58   	APPFS_PATHTYPE_INVALID,
    43     59   	APPFS_PATHTYPE_FILE,
    44     60   	APPFS_PATHTYPE_DIRECTORY,
    45     61   	APPFS_PATHTYPE_SYMLINK
    46     62   } appfs_pathtype_t;
    47     63   
           64  +/*
           65  + * AppFS Children Files linked-list
           66  + */
    48     67   struct appfs_children {
    49     68   	struct appfs_children *_next;
    50         -	int counter;
    51         -
    52     69   	char name[256];
    53     70   };
    54     71   
           72  +/*
           73  + * AppFS Path Information:
           74  + *         Completely describes a specific path, how it should be returned to
           75  + *         to the kernel
           76  + */
    55     77   struct appfs_pathinfo {
    56     78   	appfs_pathtype_t type;
    57     79   	time_t time;
    58     80   	char hostname[256];
    59     81   	int packaged;
    60     82   	unsigned long long inode;
    61     83   	union {
    62     84   		struct {
    63     85   			int childcount;
    64     86   		} dir;
    65     87   		struct {
    66     88   			int executable;
    67     89   			off_t size;
    68         -			char sha1[41];
    69     90   		} file;
    70     91   		struct {
    71     92   			off_t size;
    72     93   			char source[256];
    73     94   		} symlink;
    74     95   	} typeinfo;
    75     96   };
    76     97   
    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) {
           98  +/*
           99  + * Create a new Tcl interpreter and completely initialize it
          100  + */
          101  +static Tcl_Interp *appfs_create_TclInterp(void) {
    84    102   	Tcl_Interp *interp;
          103  +	const char *cachedir = globalThread.cachedir;
    85    104   	int tcl_ret;
    86    105   
    87    106   	APPFS_DEBUG("Creating new Tcl interpreter for TID = 0x%llx", (unsigned long long) pthread_self());
    88    107   
    89    108   	interp = Tcl_CreateInterp();
    90    109   	if (interp == NULL) {
    91    110   		fprintf(stderr, "Unable to create Tcl Interpreter.  Aborting.\n");
................................................................................
    92    111   
    93    112   		return(NULL);
    94    113   	}
    95    114   
    96    115   	tcl_ret = Tcl_Init(interp);
    97    116   	if (tcl_ret != TCL_OK) {
    98    117   		fprintf(stderr, "Unable to initialize Tcl.  Aborting.\n");
          118  +		fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp));
          119  +
          120  +		Tcl_DeleteInterp(interp);
          121  +
          122  +		return(NULL);
          123  +	}
          124  +
          125  +	tcl_ret = Tcl_Eval(interp, "package ifneeded sha1 1.0 [list load {} sha1]");
          126  +	if (tcl_ret != TCL_OK) {
          127  +		fprintf(stderr, "Unable to initialize Tcl SHA1.  Aborting.\n");
          128  +		fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp));
          129  +
          130  +		Tcl_DeleteInterp(interp);
          131  +
          132  +		return(NULL);
          133  +	}
          134  +
          135  +	tcl_ret = Tcl_Eval(interp, "package ifneeded appfsd 1.0 [list load {} appfsd]");
          136  +	if (tcl_ret != TCL_OK) {
          137  +		fprintf(stderr, "Unable to initialize Tcl AppFS Package.  Aborting.\n");
          138  +		fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp));
    99    139   
   100    140   		Tcl_DeleteInterp(interp);
   101    141   
   102    142   		return(NULL);
   103    143   	}
   104    144   
          145  +	/*
          146  +	 * Load the "appfsd.tcl" script, which is "compiled" into a C header
          147  +	 * so that it does not need to exist on the filesystem and can be
          148  +	 * directly evaluated.
          149  +	 */
   105    150   	tcl_ret = Tcl_Eval(interp, ""
   106    151   #include "appfsd.tcl.h"
   107    152   	"");
   108    153   	if (tcl_ret != TCL_OK) {
   109    154   		fprintf(stderr, "Unable to initialize Tcl AppFS script.  Aborting.\n");
   110    155   		fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp));
   111    156   
   112    157   		Tcl_DeleteInterp(interp);
   113    158   
   114    159   		return(NULL);
   115    160   	}
   116    161   
          162  +	/*
          163  +	 * Set global variables from C to Tcl
          164  +	 */
   117    165   	if (Tcl_SetVar(interp, "::appfs::cachedir", cachedir, TCL_GLOBAL_ONLY) == NULL) {
   118    166   		fprintf(stderr, "Unable to set cache directory.  This should never fail.\n");
   119    167   
   120    168   		Tcl_DeleteInterp(interp);
   121    169   
   122    170   		return(NULL);
   123    171   	}
   124    172   
          173  +	/*
          174  +	 * Initialize the "appfsd.tcl" environment, which must be done after
          175  +	 * global variables are set.
          176  +	 */
   125    177   	tcl_ret = Tcl_Eval(interp, "::appfs::init");
   126    178   	if (tcl_ret != TCL_OK) {
   127    179   		fprintf(stderr, "Unable to initialize Tcl AppFS script (::appfs::init).  Aborting.\n");
   128    180   		fprintf(stderr, "Tcl Error is: %s\n", Tcl_GetStringResult(interp));
   129    181   
   130    182   		Tcl_DeleteInterp(interp);
   131    183   
   132    184   		return(NULL);
   133    185   	}
   134    186   
   135         -	Tcl_HideCommand(interp, "glob", "glob");
   136         -	Tcl_HideCommand(interp, "exec", "exec");
   137         -	Tcl_HideCommand(interp, "pid", "pid");
          187  +	/*
          188  +	 * Hide some Tcl commands that we do not care to use and which may
          189  +	 * slow down run-time operations.
          190  +	 */
   138    191   	Tcl_HideCommand(interp, "auto_load_index", "auto_load_index");
   139    192   	Tcl_HideCommand(interp, "unknown", "unknown");
          193  +
          194  +	/*
          195  +	 * Return the completely initialized interpreter
          196  +	 */
          197  +	return(interp);
          198  +}
          199  +
          200  +/*
          201  + * Return the thread-specific Tcl interpreter, creating it if needed
          202  + */
          203  +static Tcl_Interp *appfs_TclInterp(void) {
          204  +	Tcl_Interp *interp;
          205  +	int pthread_ret;
          206  +
          207  +	interp = pthread_getspecific(interpKey);
          208  +	if (interp == NULL) {
          209  +		interp = appfs_create_TclInterp();
          210  +
          211  +		if (interp == NULL) {
          212  +			return(NULL);
          213  +		}
          214  +
          215  +		pthread_ret = pthread_setspecific(interpKey, interp);
          216  +		if (pthread_ret != 0) {
          217  +			Tcl_DeleteInterp(interp);
          218  +
          219  +			return(NULL);
          220  +		}
          221  +	}
   140    222   
   141    223   	return(interp);
   142    224   }
   143    225   
          226  +/*
          227  + * Evaluate a Tcl script constructed by concatenating a bunch of C strings
          228  + * together.
          229  + */
   144    230   static int appfs_Tcl_Eval(Tcl_Interp *interp, int objc, const char *cmd, ...) {
   145    231   	Tcl_Obj **objv;
   146    232   	const char *arg;
   147    233   	va_list argp;
   148    234   	int retval;
   149    235   	int i;
   150    236   
................................................................................
   175    261   	if (retval != TCL_OK) {
   176    262   		APPFS_DEBUG("Tcl command failed, ::errorInfo contains: %s\n", Tcl_GetVar(interp, "::errorInfo", 0));
   177    263   	}
   178    264   
   179    265   	return(retval);
   180    266   }
   181    267   
          268  +/*
          269  + * AppFS: Request that a host's package index be updated locally
          270  + */
   182    271   static void appfs_update_index(const char *hostname) {
   183    272   	Tcl_Interp *interp;
   184    273   	int tcl_ret;
   185    274   
   186    275   	APPFS_DEBUG("Enter: hostname = %s", hostname);
   187    276   
   188         -	interp = pthread_getspecific(interpKey);
          277  +	interp = appfs_TclInterp();
   189    278   	if (interp == NULL) {
   190         -		interp = appfs_create_TclInterp(globalThread.cachedir);
   191         -
   192         -		if (interp == NULL) {
   193         -			return;
   194         -		}
   195         -
   196         -		pthread_setspecific(interpKey, interp);
          279  +		return;
   197    280   	}
   198    281   
   199    282   	tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getindex", hostname);
   200    283   	if (tcl_ret != TCL_OK) {
   201    284   		APPFS_DEBUG("Call to ::appfs::getindex failed: %s", Tcl_GetStringResult(interp));
   202    285   
   203    286   		return;
   204    287   	}
   205    288   
   206    289   	return;
   207    290   }
   208    291   
          292  +/*
          293  + * AppFS: Get a SHA1 from a host
          294  + *         Returns a local file name, or NULL if it cannot be fetched
          295  + */
   209    296   static const char *appfs_getfile(const char *hostname, const char *sha1) {
   210    297   	Tcl_Interp *interp;
   211    298   	char *retval;
   212    299   	int tcl_ret;
   213    300   
   214         -	interp = pthread_getspecific(interpKey);
          301  +	interp = appfs_TclInterp();
   215    302   	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);
          303  +		return(NULL);
   223    304   	}
   224    305   
   225    306   	tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::download", hostname, sha1);
   226    307   	if (tcl_ret != TCL_OK) {
   227    308   		APPFS_DEBUG("Call to ::appfs::download failed: %s", Tcl_GetStringResult(interp));
   228    309   
   229    310   		return(NULL);
................................................................................
   230    311   	}
   231    312   
   232    313   	retval = strdup(Tcl_GetStringResult(interp));
   233    314   
   234    315   	return(retval);
   235    316   }
   236    317   
          318  +/*
          319  + * AppFS: Update the manifest for a specific package (by the package SHA1) on
          320  + * a given host
          321  + */
   237    322   static void appfs_update_manifest(const char *hostname, const char *sha1) {
   238    323   	Tcl_Interp *interp;
   239    324   	int tcl_ret;
   240    325   
   241         -	interp = pthread_getspecific(interpKey);
          326  +	interp = appfs_TclInterp();
   242    327   	if (interp == NULL) {
   243         -		interp = appfs_create_TclInterp(globalThread.cachedir);
   244         -
   245         -		if (interp == NULL) {
   246         -			return;
   247         -		}
   248         -
   249         -		pthread_setspecific(interpKey, interp);
          328  +		return;
   250    329   	}
   251    330   
   252    331   	tcl_ret = appfs_Tcl_Eval(interp, 3, "::appfs::getpkgmanifest", hostname, sha1);
   253    332   	if (tcl_ret != TCL_OK) {
   254    333   		APPFS_DEBUG("Call to ::appfs::getpkgmanifest failed: %s", Tcl_GetStringResult(interp));
   255    334   
   256    335   		return;
   257    336   	}
   258    337   
   259    338   	return;
   260    339   }
   261    340   
   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         -
          341  +/*
          342  + * Determine the UID for the user making the current FUSE filesystem request.
          343  + * This will be used to lookup the user's home directory so we can search for
          344  + * locally modified files.
          345  + */
   272    346   static uid_t appfs_get_fsuid(void) {
   273    347   	struct fuse_context *ctx;
   274    348   
   275    349   	ctx = fuse_get_context();
   276    350   	if (ctx == NULL) {
          351  +		/* Unable to lookup user for some reason */
          352  +		/* Return an unprivileged user ID */
   277    353   		return(1);
   278    354   	}
   279    355   
   280    356   	return(ctx->uid);
   281    357   }
   282    358   
   283         -static const char *appfs_get_homedir(uid_t fsuid) {
          359  +/*
          360  + * Look up the home directory for a given UID
          361  + *        Returns a C string containing the user's home directory or NULL if
          362  + *        the user's home directory does not exist or is not correctly
          363  + *        configured
          364  + */
          365  +static char *appfs_get_homedir(uid_t fsuid) {
   284    366   	struct passwd entry, *result;
   285    367   	struct stat stbuf;
   286    368   	char buf[1024], *retval;
   287    369   	int gpu_ret, stat_ret;
   288    370   
   289    371   	gpu_ret = getpwuid_r(fsuid, &entry, buf, sizeof(buf), &result);
   290    372   	if (gpu_ret != 0) {
................................................................................
   318    400   		    result->pw_dir,
   319    401   		    (unsigned long long) stbuf.st_uid
   320    402   		);
   321    403   
   322    404   		return(NULL);
   323    405   	}
   324    406   
   325         -	retval = sqlite3_mprintf("%s", result->pw_dir);
          407  +	retval = strdup(result->pw_dir);
   326    408   
   327    409   	return(retval);
   328    410   }
   329    411   
   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 */
          412  +/*
          413  + * Tcl interface to get the home directory for the user making the "current"
          414  + * FUSE I/O request
          415  + */
          416  +static int tcl_appfs_get_homedir(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) {
          417  +	char *homedir;
          418  +
          419  +        if (objc != 1) {
          420  +                Tcl_WrongNumArgs(interp, 1, objv, NULL);
          421  +                return(TCL_ERROR);
          422  +        }
          423  +
          424  +	homedir = appfs_get_homedir(appfs_get_fsuid());
          425  +
          426  +	if (homedir == NULL) {
          427  +		return(TCL_ERROR);
          428  +	}
          429  +
          430  +        Tcl_SetObjResult(interp, Tcl_NewStringObj(homedir, -1));
          431  +
          432  +	free(homedir);
          433  +
          434  +        return(TCL_OK);
          435  +}
          436  +
          437  +/*
          438  + * Generate an inode for a given path.  The inode should be computed in such
          439  + * a way that it is unlikely to be duplicated and remains the same for a given
          440  + * file
          441  + */
   759    442   static long long appfs_get_path_inode(const char *path) {
   760    443   	long long retval;
   761    444   	const char *p;
   762    445   
   763    446   	retval = 10;
   764    447   
   765    448   	for (p = path; *p; p++) {
................................................................................
   772    455   	retval %= 4294967296ULL;
   773    456   
   774    457   	return(retval);
   775    458   }
   776    459   
   777    460   /* Get information about a path, and optionally list children */
   778    461   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);
   932         -
   933         -		return(-ENOENT);
   934         -	}
   935         -
   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;
   957         -		}
   958         -
   959         -		if (children) {
   960         -			*children = dir_children;
   961         -		} else {
   962         -			appfs_free_list_children(dir_children);
   963         -		}
   964         -	}
   965         -
   966         -	free(path_s);
   967         -
   968         -	return(0);
   969    462   }
   970    463   
   971    464   static int appfs_fuse_readlink(const char *path, char *buf, size_t size) {
   972    465   	struct appfs_pathinfo pathinfo;
   973    466   	int res = 0;
   974    467   
   975    468   	APPFS_DEBUG("Enter (path = %s, ...)", path);
................................................................................
  1065    558   	filler(buf, ".", NULL, 0);
  1066    559   	filler(buf, "..", NULL, 0);
  1067    560   
  1068    561   	for (child = children; child; child = child->_next) {
  1069    562   		filler(buf, child->name, NULL, 0);
  1070    563   	}
  1071    564   
  1072         -	appfs_free_list_children(children);
          565  +//	appfs_free_list_children(children);
  1073    566   
  1074    567   	return(0);
  1075    568   }
  1076    569   
  1077    570   static int appfs_fuse_open(const char *path, struct fuse_file_info *fi) {
  1078    571   	struct appfs_pathinfo pathinfo;
  1079    572   	const char *real_path;
  1080    573   	int fh;
  1081    574   	int gpi_ret;
  1082    575   
  1083    576   	APPFS_DEBUG("Enter (path = %s, ...)", path);
          577  +
          578  +#if 0
  1084    579   
  1085    580   	if ((fi->flags & 3) != O_RDONLY) {
  1086    581                   return(-EACCES);
  1087    582   	}
  1088    583   
  1089    584   	gpi_ret = appfs_get_path_info(path, &pathinfo, NULL);
  1090    585   	if (gpi_ret != 0) {
................................................................................
  1103    598   	fh = open(real_path, O_RDONLY);
  1104    599   	free((void *) real_path);
  1105    600   	if (fh < 0) {
  1106    601   		return(-EIO);
  1107    602   	}
  1108    603   
  1109    604   	fi->fh = fh;
          605  +#endif
  1110    606   
  1111    607   	return(0);
  1112    608   }
  1113    609   
  1114    610   static int appfs_fuse_close(const char *path, struct fuse_file_info *fi) {
  1115    611   	int close_ret;
  1116    612   
................................................................................
  1134    630   	}
  1135    631   
  1136    632   	read_ret = read(fi->fh, buf, size);
  1137    633   
  1138    634   	return(read_ret);
  1139    635   }
  1140    636   
  1141         -static struct fuse_operations appfs_oper = {
          637  +/*
          638  + * SQLite3 mode: Execute raw SQL and return success or failure
          639  + */
          640  +static int appfs_sqlite3(const char *sql) {
          641  +	Tcl_Interp *interp;
          642  +	const char *sql_ret;
          643  +	int tcl_ret;
          644  +
          645  +	interp = appfs_create_TclInterp();
          646  +	if (interp == NULL) {
          647  +		fprintf(stderr, "Unable to create a Tcl interpreter.  Aborting.\n");
          648  +
          649  +		return(1);
          650  +	}
          651  +
          652  +	tcl_ret = appfs_Tcl_Eval(interp, 5, "::appfs::db", "eval", sql, "row", "unset -nocomplain row(*); parray row; puts \"----\"");
          653  +	sql_ret = Tcl_GetStringResult(interp);
          654  +
          655  +	if (tcl_ret != TCL_OK) {
          656  +		fprintf(stderr, "[error] %s\n", sql_ret);
          657  +
          658  +		return(1);
          659  +	}
          660  +
          661  +	if (sql_ret && sql_ret[0] != '\0') {
          662  +		printf("%s\n", sql_ret);
          663  +	}
          664  +
          665  +	return(0);
          666  +}
          667  +
          668  +/*
          669  + * Tcl mode: Execute raw Tcl and return success or failure
          670  + */
          671  +static int appfs_tcl(const char *tcl) {
          672  +	Tcl_Interp *interp;
          673  +	const char *tcl_result;
          674  +	int tcl_ret;
          675  +
          676  +	interp = appfs_create_TclInterp();
          677  +	if (interp == NULL) {
          678  +		fprintf(stderr, "Unable to create a Tcl interpreter.  Aborting.\n");
          679  +
          680  +		return(1);
          681  +	}
          682  +
          683  +	tcl_ret = Tcl_Eval(interp, tcl);
          684  +	tcl_result = Tcl_GetStringResult(interp);
          685  +
          686  +	if (tcl_ret != TCL_OK) {
          687  +		fprintf(stderr, "[error] %s\n", tcl_result);
          688  +
          689  +		return(1);
          690  +	}
          691  +
          692  +	if (tcl_result && tcl_result[0] != '\0') {
          693  +		printf("%s\n", tcl_result);
          694  +	}
          695  +
          696  +	return(0);
          697  +}
          698  +
          699  +/*
          700  + * AppFSd Package for Tcl:
          701  + *         Bridge for I/O operations to request information about the current
          702  + *         transaction
          703  + */
          704  +static int Appfsd_Init(Tcl_Interp *interp) {
          705  +#ifdef USE_TCL_STUBS
          706  +	if (Tcl_InitStubs(interp, TCL_VERSION, 0) == 0L) {
          707  +		return(TCL_ERROR);
          708  +	}
          709  +#endif
          710  +
          711  +	Tcl_CreateObjCommand(interp, "appfsd::get_homedir", tcl_appfs_get_homedir, NULL, NULL);
          712  +
          713  +	Tcl_PkgProvide(interp, "appfsd", "1.0");
          714  +
          715  +	return(TCL_OK);
          716  +}
          717  +
          718  +/*
          719  + * FUSE operations structure
          720  + */
          721  +static struct fuse_operations appfs_operations = {
  1142    722   	.getattr   = appfs_fuse_getattr,
  1143    723   	.readdir   = appfs_fuse_readdir,
  1144    724   	.readlink  = appfs_fuse_readlink,
  1145    725   	.open      = appfs_fuse_open,
  1146    726   	.release   = appfs_fuse_close,
  1147    727   	.read      = appfs_fuse_read
  1148    728   };
  1149    729   
          730  +/*
          731  + * FUSE option parsing callback
          732  + */
          733  +static int appfs_fuse_opt_cb(void *data, const char *arg, int key, struct fuse_args *outargs) {
          734  +	static seen_cachedir = 0;
          735  +
          736  +	if (key == FUSE_OPT_KEY_NONOPT && seen_cachedir == 0) {
          737  +		seen_cachedir = 1;
          738  +
          739  +		globalThread.cachedir = strdup(arg);
          740  +
          741  +		return(0);
          742  +	}
          743  +
          744  +	return(1);
          745  +}
          746  +
          747  +/*
          748  + * Entry point into this program.
          749  + */
  1150    750   int main(int argc, char **argv) {
  1151         -	const char *cachedir = APPFS_CACHEDIR;
  1152         -	char dbfilename[1024];
  1153         -	int pthread_ret, snprintf_ret, sqlite_ret;
          751  +	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
          752  +	int pthread_ret;
          753  +
          754  +	/*
          755  +	 * Skip passed program name
          756  +	 */
          757  +	if (argc == 0 || argv == NULL) {
          758  +		return(1);
          759  +	}
          760  +	argc--;
          761  +	argv++;
  1154    762   
  1155         -	globalThread.cachedir = cachedir;
  1156         -	globalThread.boottime = time(NULL);
  1157         -	globalThread.platform = "linux-x86_64";
          763  +	/*
          764  +	 * Set global variables, these should be configuration options.
          765  +	 */
          766  +	globalThread.cachedir = APPFS_CACHEDIR;
  1158    767   	globalThread.options.writable = 1;
  1159    768   
          769  +	/*
          770  +	 * Set global variable for "boot time" to set a time on directories
          771  +	 * that we fake.
          772  +	 */
          773  +	globalThread.boottime = time(NULL);
          774  +
          775  +	/*
          776  +	 * Register "sha1" and "appfsd" package with libtcl so that any new
          777  +	 * interpreters created (which are done dynamically by FUSE) can have
          778  +	 * the appropriate configuration done automatically.
          779  +	 */
          780  +	Tcl_StaticPackage(NULL, "sha1", Sha1_Init, NULL);
          781  +	Tcl_StaticPackage(NULL, "appfsd", Appfsd_Init, NULL);
          782  +
          783  +	/*
          784  +	 * Create a thread-specific-data (TSD) key for each thread to refer
          785  +	 * to its own Tcl interpreter.  Tcl interpreters must be unique per
          786  +	 * thread and new threads are dynamically created by FUSE.
          787  +	 */
  1160    788   	pthread_ret = pthread_key_create(&interpKey, NULL);
  1161    789   	if (pthread_ret != 0) {
  1162    790   		fprintf(stderr, "Unable to create TSD key for Tcl.  Aborting.\n");
  1163    791   
  1164    792   		return(1);
  1165    793   	}
  1166    794   
  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");
          795  +	/*
          796  +	 * Manually specify cache directory, without FUSE callback
          797  +	 * This option only works when not using FUSE, since we
          798  +	 * do not process it with FUSEs option processing.
          799  +	 */
          800  +	if (argc >= 2) {
          801  +		if (strcmp(argv[0], "--cachedir") == 0) {
          802  +			globalThread.cachedir = strdup(argv[1]);
          803  +
          804  +			argc -= 2;
          805  +			argv += 2;
          806  +		}
          807  +	}
          808  +
          809  +	/*
          810  +	 * SQLite3 mode, for running raw SQL against the cache database
          811  +	 */
          812  +	if (argc == 2 && strcmp(argv[0], "--sqlite3") == 0) {
          813  +		return(appfs_sqlite3(argv[1]));
          814  +	}
  1170    815   
  1171         -		return(1);
          816  +	/*
          817  +	 * Tcl mode, for running raw Tcl in the same environment AppFSd would
          818  +	 * run code.
          819  +	 */
          820  +	if (argc == 2 && strcmp(argv[0], "--tcl") == 0) {
          821  +		return(appfs_tcl(argv[1]));
  1172    822   	}
  1173    823   
  1174         -	sqlite_ret = sqlite3_open(dbfilename, &globalThread.db);
  1175         -	if (sqlite_ret != SQLITE_OK) {
  1176         -		fprintf(stderr, "Unable to open database: %s\n", dbfilename);
          824  +	/*
          825  +	 * Add FUSE arguments which we always supply
          826  +	 */
          827  +	fuse_opt_parse(&args, NULL, NULL, appfs_fuse_opt_cb);
          828  +	fuse_opt_add_arg(&args, "-odefault_permissions,fsname=appfs,subtype=appfsd,use_ino,kernel_cache,entry_timeout=60,attr_timeout=3600,intr,big_writes");
  1177    829   
  1178         -		return(1);
          830  +	if (getuid() == 0) {
          831  +		fuse_opt_parse(&args, NULL, NULL, NULL);
          832  +		fuse_opt_add_arg(&args, "-oallow_other");
  1179    833   	}
  1180    834   
  1181         -	return(fuse_main(argc, argv, &appfs_oper, NULL));
          835  +	/*
          836  +	 * Enter the FUSE main loop -- this will process any arguments
          837  +	 * and start servicing requests.
          838  +	 */
          839  +	return(fuse_main(args.argc, args.argv, &appfs_operations, NULL));
  1182    840   }
  1183    841    

Modified appfsd.tcl from [86cb2b92c8] to [e0ad1bdba0].

     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
    12      6   
    13      7   namespace eval ::appfs {
    14      8   	variable cachedir "/tmp/appfs-cache"
    15      9   	variable ttl 3600
    16     10   	variable nttl 60
    17     11   
    18     12   	proc _hash_sep {hash {seps 4}} {
................................................................................
    82     76   		if {![regexp {^[0-9a-f]*$} $value]} {
    83     77   			return false
    84     78   		}
    85     79   
    86     80   		return true
    87     81   	}
    88     82   
    89         -	proc _db {args} {
    90         -		return [uplevel 1 [list ::appfs::db {*}$args]]
    91         -	}
    92         -
    93     83   	proc _normalizeOS {os} {
    94     84   		set os [string tolower [string trim $os]]
    95     85   
    96     86   		switch -- $os {
    97     87   			"linux" - "freebsd" - "openbsd" - "netbsd" {
    98     88   				return $os
    99     89   			}
................................................................................
   126    116   		return -code error "Unable to normalize CPU: $cpu"
   127    117   	}
   128    118   
   129    119   	proc init {} {
   130    120   		if {[info exists ::appfs::init_called]} {
   131    121   			return
   132    122   		}
          123  +
          124  +		# Force [parray] to be loaded
          125  +		catch {
          126  +			parray does_not_exist
          127  +		}
   133    128   
   134    129   		set ::appfs::init_called 1
   135    130   
   136    131   		if {![info exists ::appfs::db]} {
   137    132   			file mkdir $::appfs::cachedir
   138    133   
   139    134   			sqlite3 ::appfs::db [file join $::appfs::cachedir cache.db]
   140    135   		}
   141    136   
   142    137   		# 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);}
          138  +		db eval {CREATE TABLE IF NOT EXISTS sites(hostname PRIMARY KEY, lastUpdate, ttl);}
          139  +		db eval {CREATE TABLE IF NOT EXISTS packages(hostname, sha1, package, version, os, cpuArch, isLatest, haveManifest);}
          140  +		db eval {CREATE TABLE IF NOT EXISTS files(package_sha1, type, time, source, size, perms, file_sha1, file_name, file_directory);}
   146    141   
   147    142   		# 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);}
          143  +		db eval {CREATE INDEX IF NOT EXISTS sites_index ON sites (hostname);}
          144  +		db eval {CREATE INDEX IF NOT EXISTS packages_index ON packages (hostname, package, version, os, cpuArch);}
          145  +		db eval {CREATE INDEX IF NOT EXISTS files_index ON files (package_sha1, file_name, file_directory);}
   151    146   	}
   152    147   
   153    148   	proc download {hostname hash {method sha1}} {
   154    149   		set url "http://$hostname/appfs/$method/$hash"
   155    150   		set file [_cachefile $url $hash]
   156    151   
   157    152   		if {![file exists $file]} {
................................................................................
   160    155   
   161    156   		return $file
   162    157   	}
   163    158   
   164    159   	proc getindex {hostname} {
   165    160   		set now [clock seconds]
   166    161   
   167         -		set lastUpdates [_db eval {SELECT lastUpdate, ttl FROM sites WHERE hostname = $hostname LIMIT 1;}]
          162  +		set lastUpdates [db eval {SELECT lastUpdate, ttl FROM sites WHERE hostname = $hostname LIMIT 1;}]
   168    163   		if {[llength $lastUpdates] == 0} {
   169    164   			set lastUpdate 0
   170    165   			set ttl 0
   171    166   		} else {
   172    167   			set lastUpdate [lindex $lastUpdates 0]
   173    168   			set ttl [lindex $lastUpdates 1]
   174    169   		}
................................................................................
   190    185   			}
   191    186   			::http::reset $token
   192    187   			::http::cleanup $token
   193    188   		}
   194    189   
   195    190   		if {![info exists indexhash_data]} {
   196    191   			# Cache this result for 60 seconds
   197         -			_db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::nttl);}
          192  +			db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::nttl);}
   198    193   
   199    194   			return -code error "Unable to fetch $url"
   200    195   		}
   201    196   
   202    197   		set indexhash [lindex [split $indexhash_data ","] 0]
   203    198   
   204    199   		if {![_isHash $indexhash]} {
................................................................................
   240    235   			if {![_isHash $pkgInfo(hash)]} {
   241    236   				continue
   242    237   			}
   243    238   
   244    239   			lappend curr_packages $pkgInfo(hash)
   245    240   
   246    241   			# 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);}]
          242  +			set existing_packages [db eval {SELECT package FROM packages WHERE hostname = $hostname AND sha1 = $pkgInfo(hash);}]
   248    243   			if {[lsearch -exact $existing_packages $pkgInfo(package)] != -1} {
   249    244   				continue
   250    245   			}
   251    246   
   252    247   			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);}
          248  +				db eval {UPDATE packages SET isLatest = 0 WHERE hostname = $hostname AND package = $pkgInfo($package) AND os = $pkgInfo($package) AND cpuArch = $pkgInfo(cpuArch);}
   254    249   			}
   255    250   
   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);}
          251  +			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    252   		}
   258    253   
   259    254   		# Look for packages that have been deleted
   260         -		set found_packages [_db eval {SELECT sha1 FROM packages WHERE hostname = $hostname;}]
          255  +		set found_packages [db eval {SELECT sha1 FROM packages WHERE hostname = $hostname;}]
   261    256   		foreach package $found_packages {
   262    257   			set found_packages_arr($package) 1
   263    258   		}
   264    259   
   265    260   		foreach package $curr_packages {
   266    261   			unset -nocomplain found_packages_arr($package)
   267    262   		}
   268    263   
   269    264   		foreach package [array names found_packages_arr] {
   270         -			_db eval {DELETE FROM packages WHERE hostname = $hostname AND sha1 = $package;}
          265  +			db eval {DELETE FROM packages WHERE hostname = $hostname AND sha1 = $package;}
   271    266   		}
   272    267   
   273         -		_db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::ttl);}
          268  +		db eval {INSERT OR REPLACE INTO sites (hostname, lastUpdate, ttl) VALUES ($hostname, $now, $::appfs::ttl);}
   274    269   
   275    270   		return COMPLETE
   276    271   	}
   277    272   
   278    273   	proc getpkgmanifest {hostname package_sha1} {
   279         -		set haveManifests [_db eval {SELECT haveManifest FROM packages WHERE sha1 = $package_sha1 LIMIT 1;}]
          274  +		set haveManifests [db eval {SELECT haveManifest FROM packages WHERE sha1 = $package_sha1 LIMIT 1;}]
   280    275   		set haveManifest [lindex $haveManifests 0]
   281    276   
   282    277   		if {$haveManifest} {
   283    278   			return COMPLETE
   284    279   		}
   285    280   
   286    281   		if {![_isHash $package_sha1]} {
................................................................................
   288    283   		}
   289    284   
   290    285   		set file [download $hostname $package_sha1]
   291    286   		set fd [open $file]
   292    287   		set pkgdata [read $fd]
   293    288   		close $fd
   294    289   
   295         -		_db transaction {
          290  +		db transaction {
   296    291   			foreach line [split $pkgdata "\n"] {
   297    292   				set line [string trim $line]
   298    293   
   299    294   				if {$line == ""} {
   300    295   					continue
   301    296   				}
   302    297   
................................................................................
   322    317   				}
   323    318   
   324    319   				set fileInfo(name) [join $work ","]
   325    320   				set fileInfo(name) [split [string trim $fileInfo(name) "/"] "/"]
   326    321   				set fileInfo(directory) [join [lrange $fileInfo(name) 0 end-1] "/"]
   327    322   				set fileInfo(name) [lindex $fileInfo(name) end]
   328    323   
   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;}
          324  +				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) );}
          325  +				db eval {UPDATE packages SET haveManifest = 1 WHERE sha1 = $package_sha1;}
   331    326   			}
   332    327   		}
   333    328   
   334    329   		return COMPLETE
   335    330   	}
   336    331   }

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  +}