Check-in [f66a795908]
Overview
Comment:Updated with basic functionality
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:f66a795908579d168b2cde27bc84a0adde122de1
User & Date: rkeene on 2014-09-09 06:01:49
Other Links: manifest | tags
Context
2014-09-09
06:10
Added executable bit check-in: 99c9d35a89 user: rkeene tags: trunk
06:01
Updated with basic functionality check-in: f66a795908 user: rkeene tags: trunk
03:51
Updated to supply valid linked list for children and added readdir implementation check-in: f31f4f56c6 user: rkeene tags: trunk
Changes

Modified appfs.c from [190e7becb3] to [fb14cea935].

     1      1   #define FUSE_USE_VERSION 26
     2      2   
     3      3   #include <sqlite3.h>
     4      4   #include <string.h>
     5      5   #include <stdarg.h>
            6  +#include <stdlib.h>
     6      7   #include <errno.h>
     7      8   #include <fcntl.h>
     8      9   #include <stdio.h>
     9     10   #include <fuse.h>
    10     11   #include <tcl.h>
    11     12   
    12     13   #define APPFS_CACHEDIR "/tmp/appfs-cache"
    13     14   
    14         -#define APPFS_DEBUG(x...) { fprintf(stderr, "%i:%s: ", __LINE__, __func__); fprintf(stderr, x); fprintf(stderr, "\n"); }
           15  +#define APPFS_DEBUG(x...) { fprintf(stderr, "[debug] %s:%i:%s: ", __FILE__, __LINE__, __func__); fprintf(stderr, x); fprintf(stderr, "\n"); }
    15     16   
    16     17   struct appfs_thread_data {
    17     18   	Tcl_Interp *interp;
    18     19   	sqlite3 *db;
    19     20   };
    20     21   
    21     22   struct appfs_thread_data globalThread;
................................................................................
    35     36   	APPFS_CPU_ALL,
    36     37   	APPFS_CPU_AMD64,
    37     38   	APPFS_CPU_I386,
    38     39   	APPFS_CPU_ARM
    39     40   } appfs_cpuArch_t;
    40     41   
    41     42   typedef enum {
           43  +	APPFS_PATHTYPE_INVALID,
    42     44   	APPFS_PATHTYPE_FILE,
    43     45   	APPFS_PATHTYPE_DIRECTORY,
    44     46   	APPFS_PATHTYPE_SYMLINK
    45     47   } appfs_pathtype_t;
    46     48   
    47     49   struct appfs_package {
    48     50   	struct appfs_package *_next;
................................................................................
   187    189   		Tcl_DecrRefCount(objv[i]);
   188    190   	}
   189    191   
   190    192   	ckfree((void *) objv);
   191    193   
   192    194   	return(retval);
   193    195   }
          196  +
          197  +static int appfs_update_index(const char *hostname) {
          198  +	int tcl_ret;
          199  +
          200  +	tcl_ret = appfs_Tcl_Eval(globalThread.interp, 2, "::appfs::getindex", hostname);
          201  +	if (tcl_ret != TCL_OK) {
          202  +		APPFS_DEBUG("Call to ::appfs::getindex failed: %s", Tcl_GetStringResult(globalThread.interp));
          203  +
          204  +		return(-1);
          205  +	}
          206  +
          207  +	return(0);
          208  +}
          209  +
          210  +static int appfs_getfile(const char *hostname, const char *sha1) {
          211  +	return(0);
          212  +}
          213  +
          214  +static int appfs_update_manifest(const char *hostname, const char *sha1) {
          215  +	int tcl_ret;
          216  +
          217  +	tcl_ret = appfs_Tcl_Eval(globalThread.interp, 3, "::appfs::getpkgmanifest", hostname, sha1);
          218  +	if (tcl_ret != TCL_OK) {
          219  +		APPFS_DEBUG("Call to ::appfs::getpkgmanifest failed: %s", Tcl_GetStringResult(globalThread.interp));
          220  +
          221  +		return(-1);
          222  +	}
          223  +
          224  +	return(0);
          225  +}
          226  +
   194    227   
   195    228   #define appfs_free_list_type(id, type) static void appfs_free_list_ ## id(type *head) { \
   196    229   	type *obj, *next; \
   197    230   	for (obj = head; obj; obj = next) { \
   198    231   		next = obj->_next; \
   199    232   		ckfree((void *) obj); \
   200    233   	} \
................................................................................
   275    308   
   276    309   	return(0);
   277    310   }
   278    311   
   279    312   static struct appfs_package *appfs_getindex(const char *hostname, int *package_count_p) {
   280    313   	struct appfs_package *head = NULL;
   281    314   	char *sql;
   282         -	int tcl_ret, sqlite_ret;
          315  +	int index_ret, sqlite_ret;
   283    316   
   284    317   	if (package_count_p == NULL) {
   285    318   		return(NULL);
   286    319   	}
   287    320   
   288         -	tcl_ret = appfs_Tcl_Eval(globalThread.interp, 2, "::appfs::getindex", hostname);
   289         -	if (tcl_ret != TCL_OK) {
   290         -		APPFS_DEBUG("Call to ::appfs::getindex failed: %s", Tcl_GetStringResult(globalThread.interp));
   291         -
          321  +	index_ret = appfs_update_index(hostname);
          322  +	if (index_ret != 0) {
   292    323   		return(NULL);
   293    324   	}
   294    325   
   295    326   	sql = sqlite3_mprintf("SELECT package, version, sha1, os, cpuArch, isLatest FROM packages WHERE hostname = %Q;", hostname);
   296    327   	if (sql == NULL) {
   297    328   		APPFS_DEBUG("Call to sqlite3_mprintf failed.");
   298    329   
................................................................................
   311    342   	if (head != NULL) {
   312    343   		*package_count_p = head->counter + 1;
   313    344   	}
   314    345   
   315    346   	return(head);
   316    347   }
   317    348   
   318         -static int appfs_getfile(const char *hostname, const char *sha1) {
          349  +static int appfs_getchildren_cb(void *_head, int columns, char **values, char **names) {
          350  +	struct appfs_children **head_p, *obj;
          351  +
          352  +	head_p = _head;
          353  +
          354  +	obj = (void *) ckalloc(sizeof(*obj));
          355  +
          356  +	snprintf(obj->name, sizeof(obj->name), "%s", values[0]);
          357  +
          358  +	if (*head_p == NULL) {
          359  +		obj->counter = 0;
          360  +	} else {
          361  +		obj->counter = (*head_p)->counter + 1;
          362  +	}
          363  +
          364  +	obj->_next = *head_p;
          365  +	*head_p = obj;
          366  +
          367  +	return(0);
          368  +	
          369  +}
          370  +
          371  +static struct appfs_children *appfs_getchildren(const char *hostname, const char *package_hash, const char *path, int *children_count_p) {
          372  +	struct appfs_children *head = NULL;
          373  +	char *sql;
          374  +	int index_ret, sqlite_ret, manifest_ret;
          375  +
          376  +	if (children_count_p == NULL) {
          377  +		return(NULL);
          378  +	}
          379  +
          380  +	index_ret = appfs_update_index(hostname);
          381  +	if (index_ret != 0) {
          382  +		return(NULL);
          383  +	}
          384  +
          385  +	manifest_ret = appfs_update_manifest(hostname, package_hash);
          386  +	if (manifest_ret != 0) {
          387  +		return(NULL);
          388  +	}
          389  +
          390  +	sql = sqlite3_mprintf("SELECT file_name FROM files WHERE package_sha1 = %Q AND file_directory = %Q;", package_hash, path);
          391  +	if (sql == NULL) {
          392  +		APPFS_DEBUG("Call to sqlite3_mprintf failed.");
          393  +
          394  +		return(NULL);
          395  +	}
          396  +
          397  +	APPFS_DEBUG("SQL: %s", sql);
          398  +	sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_getchildren_cb, &head, NULL);
          399  +	sqlite3_free(sql);
          400  +
          401  +	if (sqlite_ret != SQLITE_OK) {
          402  +		APPFS_DEBUG("Call to sqlite3_exec failed.");
          403  +
          404  +		return(NULL);
          405  +	}
          406  +
          407  +	if (head != NULL) {
          408  +		*children_count_p = head->counter + 1;
          409  +	}
          410  +
          411  +	return(head);
          412  +}
          413  +
          414  +static int appfs_lookup_package_hash_cb(void *_retval, int columns, char **values, char **names) {
          415  +	char **retval = _retval;
          416  +
          417  +	*retval = strdup(values[0]);
          418  +
          419  +	return(0);
          420  +}
          421  +
          422  +static char *appfs_lookup_package_hash(const char *hostname, const char *package, appfs_os_t os, appfs_cpuArch_t cpuArch, const char *version) {
          423  +	char *sql;
          424  +	char *retval = NULL;
          425  +	int index_ret, sqlite_ret;
          426  +
          427  +	index_ret = appfs_update_index(hostname);
          428  +	if (index_ret != 0) {
          429  +		return(NULL);
          430  +	}
          431  +
          432  +	sql = sqlite3_mprintf("SELECT sha1 FROM packages WHERE hostname = %Q AND package = %Q AND os = %Q AND cpuArch = %Q AND version = %Q;",
          433  +		hostname,
          434  +		package,
          435  +		appfs_convert_os_toString(os),
          436  +		appfs_convert_cpuArch_toString(cpuArch),
          437  +		version
          438  +	);
          439  +	if (sql == NULL) {
          440  +		APPFS_DEBUG("Call to sqlite3_mprintf failed.");
          441  +
          442  +		return(NULL);
          443  +	}
          444  +
          445  +	APPFS_DEBUG("SQL: %s", sql);
          446  +	sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_lookup_package_hash_cb, &retval, NULL);
          447  +	sqlite3_free(sql);
          448  +
          449  +	if (sqlite_ret != SQLITE_OK) {
          450  +		APPFS_DEBUG("Call to sqlite3_exec failed.");
          451  +
          452  +		return(NULL);
          453  +	}
          454  +
          455  +	return(retval);
          456  +}
          457  +
          458  +static int appfs_getfileinfo_cb(void *_pathinfo, int columns, char **values, char **names) {
          459  +	struct appfs_pathinfo *pathinfo = _pathinfo;
          460  +	const char *type, *time, *source, *size;
          461  +
          462  +	type = values[0];
          463  +	time = values[1];
          464  +	source = values[2];
          465  +	size = values[3];
          466  +
          467  +	pathinfo->time = strtoull(time, NULL, 10);
          468  +
          469  +	if (strcmp(type, "file") == 0) {
          470  +		pathinfo->type = APPFS_PATHTYPE_FILE;
          471  +		pathinfo->typeinfo.file.executable = 0;
          472  +		pathinfo->typeinfo.file.size = strtoull(size, NULL, 10);
          473  +
          474  +		return(0);
          475  +	}
          476  +
          477  +	if (strcmp(type, "directory") == 0) {
          478  +		pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
          479  +		pathinfo->typeinfo.dir.childcount = 0;
          480  +
          481  +		return(0);
          482  +	}
          483  +
   319    484   	return(0);
          485  +
          486  +	/* Until this is used, prevent the compiler from complaining */
          487  +	source = source;
   320    488   }
   321    489   
   322         -static int appfs_getmanifest(const char *hostname, const char *sha1) {
          490  +static int appfs_getfileinfo(const char *hostname, const char *package_hash, const char *_path, struct appfs_pathinfo *pathinfo) {
          491  +	char *directory, *file, *path;
          492  +	char *sql;
          493  +	int index_ret, sqlite_ret, manifest_ret;
          494  +
          495  +	if (pathinfo == NULL) {
          496  +		return(-EIO);
          497  +	}
          498  +
          499  +	index_ret = appfs_update_index(hostname);
          500  +	if (index_ret != 0) {
          501  +		return(-EIO);
          502  +	}
          503  +
          504  +	manifest_ret = appfs_update_manifest(hostname, package_hash);
          505  +	if (manifest_ret != 0) {
          506  +		return(-EIO);
          507  +	}
          508  +
          509  +	path = strdup(_path);
          510  +	directory = path;
          511  +	file = strrchr(path, '/');
          512  +	if (file == NULL) {
          513  +		file = path;
          514  +		directory = "";
          515  +	} else {
          516  +		*file = '\0';
          517  +		file++;
          518  +	}
          519  +
          520  +	sql = sqlite3_mprintf("SELECT type, time, source, size FROM files WHERE package_sha1 = %Q AND file_directory = %Q AND file_name = %Q;", package_hash, directory, file);
          521  +	if (sql == NULL) {
          522  +		APPFS_DEBUG("Call to sqlite3_mprintf failed.");
          523  +
          524  +		free(path);
          525  +
          526  +		return(-EIO);
          527  +	}
          528  +
          529  +	free(path);
          530  +
          531  +	pathinfo->type = APPFS_PATHTYPE_INVALID;
          532  +
          533  +	APPFS_DEBUG("SQL: %s", sql);
          534  +	sqlite_ret = sqlite3_exec(globalThread.db, sql, appfs_getfileinfo_cb, pathinfo, NULL);
          535  +	sqlite3_free(sql);
          536  +
          537  +	if (sqlite_ret != SQLITE_OK) {
          538  +		APPFS_DEBUG("Call to sqlite3_exec failed.");
          539  +
          540  +		return(-EIO);
          541  +	}
          542  +
          543  +	if (pathinfo->type == APPFS_PATHTYPE_INVALID) {
          544  +		return(-ENOENT);
          545  +	}
          546  +
   323    547   	return(0);
   324    548   }
   325    549   
   326    550   /* Get information about a path, and optionally list children */
   327    551   static int appfs_get_path_info(const char *_path, struct appfs_pathinfo *pathinfo, struct appfs_children **children) {
   328    552   	struct appfs_site *sites, *site;
   329         -	struct appfs_children *node;
   330         -	int sites_count;
   331         -	char *path, *hostname, *package;
          553  +	struct appfs_children *node, *dir_children;
          554  +	struct appfs_package *packages, *package;
          555  +	appfs_os_t os_val;
          556  +	appfs_cpuArch_t cpuArch_val;
          557  +	char *hostname, *packagename, *os_cpuArch, *os, *cpuArch, *version;
          558  +	char *path, *path_s;
          559  +	char *package_hash;
          560  +	int sites_count, packages_count, os_cpuArch_count, version_count, files_count;
          561  +	int fileinfo_ret;
          562  +
          563  +	if (children) {
          564  +		*children = NULL;
          565  +	}
   332    566   
   333    567   	if (_path == NULL) {
   334    568   		return(-ENOENT);
   335    569   	}
   336    570   
   337    571   	if (_path[0] != '/') {
   338    572   		return(-ENOENT);
   339    573   	}
   340    574   
   341    575   	if (_path[1] == '\0') {
   342    576   		/* Request for the root directory */
   343         -		pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
   344    577   		sites = appfs_getsites(&sites_count);
          578  +
          579  +		pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
   345    580   		pathinfo->typeinfo.dir.childcount = sites_count;
   346    581   
   347    582   		if (children) {
   348         -			*children = NULL;
   349    583   			for (site = sites; site; site = site->_next) {
   350    584   				node = (void *) ckalloc(sizeof(*node));
   351    585   				node->_next = *children;
   352    586   				strcpy(node->name, site->name);
   353    587   				*children = node;
   354    588   			}
   355    589   		}
................................................................................
   356    590   
   357    591   		appfs_free_list_site(sites);
   358    592   
   359    593   		return(0);
   360    594   	}
   361    595   
   362    596   	path = strdup(_path);
          597  +	path_s = path;
   363    598   
   364    599   	hostname = path + 1;
   365         -	package = strchr(hostname, '/');
          600  +	packagename = strchr(hostname, '/');
   366    601   
   367         -	if (package == NULL) {
          602  +	if (packagename != NULL) {
          603  +		*packagename = '\0';
          604  +		packagename++;
          605  +	}
          606  +
          607  +	packages = appfs_getindex(hostname, &packages_count);
          608  +
          609  +	if (packages == NULL || packages_count == 0) {
          610  +		APPFS_DEBUG("Unable to fetch package index from %s", hostname);
          611  +
          612  +		free(path_s);
          613  +
          614  +		return(-ENOENT);
          615  +	}
          616  +
          617  +	if (packagename == NULL) {
   368    618   		/* Request for a single hostname */
          619  +		pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
          620  +		pathinfo->typeinfo.dir.childcount = packages_count;
          621  +
          622  +		if (children) {
          623  +			for (package = packages; package; package = package->_next) {
          624  +				node = (void *) ckalloc(sizeof(*node));
          625  +				node->_next = *children;
          626  +				strcpy(node->name, package->name);
          627  +				*children = node;
          628  +			}
          629  +		}
          630  +
          631  +		appfs_free_list_package(packages);
          632  +
          633  +		free(path_s);
          634  +
          635  +		return(0);
          636  +	}
          637  +
          638  +	os_cpuArch = strchr(packagename, '/');
          639  +	if (os_cpuArch == NULL) {
          640  +		/* Request for OS and CPU Arch for a specific package */
          641  +		pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
          642  +
          643  +		os_cpuArch_count = 0;
          644  +		for (package = packages; package; package = package->_next) {
          645  +			if (strcmp(package->name, packagename) != 0) {
          646  +				APPFS_DEBUG("Skipping package named \"%s\", does not match requested package: \"%s\"", package->name, packagename);
          647  +
          648  +				continue;
          649  +			}
          650  +
          651  +			os_cpuArch_count++;
          652  +
          653  +			if (children) {
          654  +				node = (void *) ckalloc(sizeof(*node));
          655  +				node->_next = *children;
          656  +				snprintf(node->name, sizeof(node->name), "%s-%s", appfs_convert_os_toString(package->os), appfs_convert_cpuArch_toString(package->cpuArch));
          657  +				*children = node;
          658  +			}
          659  +		}
          660  +
          661  +		appfs_free_list_package(packages);
          662  +
          663  +		free(path_s);
          664  +
          665  +		pathinfo->typeinfo.dir.childcount = os_cpuArch_count;
          666  +
          667  +		return(0);
          668  +	}
          669  +
          670  +	*os_cpuArch = '\0';
          671  +	os_cpuArch++;
          672  +
          673  +	version = strchr(os_cpuArch, '/');
          674  +
          675  +	if (version != NULL) {
          676  +		*version = '\0';
          677  +		version++;
          678  +	}
          679  +
          680  +	os = os_cpuArch;
          681  +	cpuArch = strchr(os_cpuArch, '-');
          682  +	if (cpuArch) {
          683  +		*cpuArch = '\0';
          684  +		cpuArch++;
          685  +
          686  +		cpuArch_val = appfs_convert_cpuArch_fromString(cpuArch);
          687  +	} else {
          688  +		cpuArch_val = APPFS_CPU_UNKNOWN;
          689  +	}
          690  +
          691  +	os_val = appfs_convert_os_fromString(os);
          692  +
          693  +	if (version == NULL) {
          694  +		/* Request for version list for a package on an OS/CPU */
          695  +		pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
          696  +
          697  +		version_count = 0;
          698  +		for (package = packages; package; package = package->_next) {
          699  +			if (strcmp(package->name, packagename) != 0) {
          700  +				APPFS_DEBUG("Skipping package named \"%s\", does not match requested package: \"%s\"", package->name, packagename);
          701  +
          702  +				continue;
          703  +			}
          704  +
          705  +			if (package->os != os_val) {
          706  +				continue;
          707  +			}
          708  +
          709  +			if (package->cpuArch != cpuArch_val) {
          710  +				continue;
          711  +			}
          712  +
          713  +			version_count++;
          714  +
          715  +			if (children) {
          716  +				node = (void *) ckalloc(sizeof(*node));
          717  +				node->_next = *children;
          718  +				strcpy(node->name, package->version);
          719  +				*children = node;
          720  +			}
          721  +		}
          722  +
          723  +		appfs_free_list_package(packages);
          724  +
          725  +		free(path_s);
          726  +
          727  +		pathinfo->typeinfo.dir.childcount = version_count;
          728  +
          729  +		return(0);
          730  +	}
          731  +
          732  +	appfs_free_list_package(packages);
          733  +
          734  +	path = strchr(version, '/');
          735  +	if (path == NULL) {
          736  +		path = "";
          737  +	} else {
          738  +		*path = '\0';
          739  +		path++;
          740  +	}
          741  +
          742  +	/* Request for a file in a specific package */
          743  +	APPFS_DEBUG("Requesting information for hostname = %s, package = %s, os = %s, cpuArch = %s, version = %s, path = %s", 
          744  +		hostname, packagename, os, cpuArch, version, path
          745  +	);
          746  +
          747  +	package_hash = appfs_lookup_package_hash(hostname, packagename, os_val, cpuArch_val, version);
          748  +	if (package_hash == NULL) {
          749  +		free(path_s);
          750  +
          751  +		return(-ENOENT);
          752  +	}
          753  +
          754  +	APPFS_DEBUG("  ... which hash a hash of %s", package_hash);
          755  +
          756  +	if (strcmp(path, "") == 0) {
          757  +		pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
   369    758   	} else {
   370         -		*package = '\0';
   371         -		package++;
          759  +		fileinfo_ret = appfs_getfileinfo(hostname, package_hash, path, pathinfo);
          760  +		if (fileinfo_ret != 0) {
          761  +			free(path_s);
          762  +
          763  +			return(fileinfo_ret);
   372    764   	}
   373    765   }
          766  +
          767  +	if (pathinfo->type == APPFS_PATHTYPE_DIRECTORY) {
          768  +		dir_children = appfs_getchildren(hostname, package_hash, path, &files_count);
          769  +
          770  +		if (dir_children != NULL) {
          771  +			pathinfo->typeinfo.dir.childcount = files_count;
          772  +		}
          773  +
          774  +		if (children) {
          775  +			*children = dir_children;
          776  +		}
          777  +	}
          778  +
          779  +	free(path_s);
          780  +
          781  +	return(0);
          782  +}
   374    783   
   375    784   static int appfs_fuse_getattr(const char *path, struct stat *stbuf) {
   376    785   	struct appfs_pathinfo pathinfo;
   377    786   	int res = 0;
   378    787   
   379    788   	APPFS_DEBUG("Enter (path = %s, ...)", path);
   380    789   
................................................................................
   438    847   	return(-ENOENT);
   439    848   }
   440    849   
   441    850   #ifdef APPFS_TEST_DRIVER
   442    851   static int appfs_test_driver(void) {
   443    852   	struct appfs_site *sites, *site;
   444    853   	struct appfs_package *packages, *package;
   445         -	int packages_count = 0, sites_count = 0;
          854  +	struct appfs_children *files, *file;
          855  +	char *sha1 = NULL;
          856  +	int packages_count = 0, sites_count = 0, files_count;
   446    857   
   447    858   	sites = appfs_getsites(&sites_count);
   448    859   	printf("Sites:\n");
   449    860   	for (site = sites; site; site = site->_next) {
   450    861   		printf("\tname = %s\n", site->name);
   451    862   	}
   452    863   
................................................................................
   456    867   	if (packages == NULL || packages_count == 0) {
   457    868   		fprintf(stderr, "Unable to fetch package index from rkeene.org.\n");
   458    869   
   459    870   		return(1);
   460    871   	}
   461    872   
   462    873   	for (package = packages; package; package = package->_next) {
          874  +		sha1 = package->sha1;
          875  +
   463    876   		printf("Package:\n\tname = %s\n\tversion = %s\n\tsha1 = %s\n\tos = %s\n\tcpuArch = %s\n",
   464    877   			package->name,
   465    878   			package->version,
   466    879   			package->sha1,
   467    880   			appfs_convert_os_toString(package->os),
   468    881   			appfs_convert_cpuArch_toString(package->cpuArch)
   469    882   		);
   470    883   	}
   471    884   
          885  +	files = appfs_getchildren("rkeene.org", sha1, "", &files_count);
          886  +	if (files == NULL) {
          887  +		fprintf(stderr, "Unable to list files in the last package.\n");
          888  +
          889  +		return(1);
          890  +	}
          891  +
          892  +	printf("Files:\n");
          893  +	for (file = files; file; file = file->_next) {
          894  +		printf("\t%s\n", file->name);
          895  +	}
          896  +
          897  +	appfs_free_list_children(files);
          898  +
          899  +	files = appfs_getchildren("rkeene.org", sha1, "tcl", &files_count);
          900  +	if (files == NULL) {
          901  +		fprintf(stderr, "Unable to list files in the last package.\n");
          902  +
          903  +		return(1);
          904  +	}
          905  +
          906  +	printf("Files:\n");
          907  +	for (file = files; file; file = file->_next) {
          908  +		printf("\ttcl/%s\n", file->name);
          909  +	}
          910  +
          911  +	appfs_free_list_children(files);
   472    912   	appfs_free_list_package(packages);
   473    913   
   474    914   	return(0);
   475    915   }
   476    916   #else
   477    917   static struct fuse_operations appfs_oper = {
   478    918   	.getattr	= appfs_fuse_getattr,

Modified appfs.tcl from [6d048578b4] to [604641f8e6].

   161    161   		set fd [open $file]
   162    162   		set pkgdata [read $fd]
   163    163   		close $fd
   164    164   
   165    165   		foreach line [split $pkgdata "\n"] {
   166    166   			set line [string trim $line]
   167    167   
   168         -			if {[string match "*/*" $line]} {
   169         -				continue
   170         -			}
   171         -
   172    168   			if {$line == ""} {
   173    169   				continue
   174    170   			}
   175    171   
   176    172   			set work [split $line ","]
   177    173   
   178    174   			unset -nocomplain fileInfo