Check-in [d64c2e9bf7]
Overview
Comment:Add "getattr" implementation
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | tcl-ops
Files: files | file ages | folders
SHA1:d64c2e9bf72d38141c79e99db0cb0ee2a56c3220
User & Date: rkeene on 2014-11-07 11:36:59
Other Links: manifest | tags
Context
2014-11-07
12:13
Added basic "open" support check-in: ebbca87b7e user: rkeene tags: tcl-ops
11:36
Add "getattr" implementation check-in: d64c2e9bf7 user: rkeene tags: tcl-ops
08:48
Added basic "getchildren" implementation in Tcl check-in: ee13ee5aa4 user: rkeene tags: tcl-ops
Changes

Modified appfsd.c from [16b07e2510] to [70718fd2dc].

    39     39   
    40     40   /*
    41     41    * Global variables, needed for all threads but only initialized before any
    42     42    * FUSE threads are created
    43     43    */
    44     44   const char *appfs_cachedir;
    45     45   time_t appfs_boottime;
           46  +int appfs_fuse_started = 0;
    46     47   
    47     48   /*
    48     49    * AppFS Path Type:  Describes the type of path a given file is
    49     50    */
    50     51   typedef enum {
    51     52   	APPFS_PATHTYPE_INVALID,
    52     53   	APPFS_PATHTYPE_FILE,
    53     54   	APPFS_PATHTYPE_DIRECTORY,
    54     55   	APPFS_PATHTYPE_SYMLINK
    55     56   } appfs_pathtype_t;
    56     57   
    57         -/*
    58         - * AppFS Children Files linked-list
    59         - */
    60         -struct appfs_children {
    61         -	struct appfs_children *_next;
    62         -	char name[256];
    63         -};
    64         -
    65     58   /*
    66     59    * AppFS Path Information:
    67     60    *         Completely describes a specific path, how it should be returned to
    68     61    *         to the kernel
    69     62    */
    70     63   struct appfs_pathinfo {
    71     64   	appfs_pathtype_t type;
................................................................................
   260    253   /*
   261    254    * Determine the UID for the user making the current FUSE filesystem request.
   262    255    * This will be used to lookup the user's home directory so we can search for
   263    256    * locally modified files.
   264    257    */
   265    258   static uid_t appfs_get_fsuid(void) {
   266    259   	struct fuse_context *ctx;
          260  +
          261  +	if (!appfs_fuse_started) {
          262  +		return(getuid());
          263  +	}
   267    264   
   268    265   	ctx = fuse_get_context();
   269    266   	if (ctx == NULL) {
   270    267   		/* Unable to lookup user for some reason */
   271    268   		/* Return an unprivileged user ID */
   272    269   		return(1);
   273    270   	}
................................................................................
   373    370   	retval += 10;
   374    371   	retval %= 4294967296ULL;
   375    372   
   376    373   	return(retval);
   377    374   }
   378    375   
   379    376   /* Get information about a path, and optionally list children */
   380         -static int appfs_get_path_info(const char *_path, struct appfs_pathinfo *pathinfo, struct appfs_children **children) {
          377  +static int appfs_get_path_info(const char *path, struct appfs_pathinfo *pathinfo) {
          378  +	Tcl_Interp *interp;
          379  +	Tcl_Obj *attrs_dict, *attr_value;
          380  +	const char *attr_value_str;
          381  +	Tcl_WideInt attr_value_wide;
          382  +	int attr_value_int;
          383  +	static __thread Tcl_Obj *attr_key_type = NULL, *attr_key_perms = NULL, *attr_key_size = NULL, *attr_key_time = NULL, *attr_key_source = NULL, *attr_key_childcount = NULL;
          384  +	int tcl_ret;
          385  +
          386  +	interp = appfs_TclInterp();
          387  +	if (interp == NULL) {
          388  +		return(1);
          389  +	}
          390  +
          391  +	tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getattr", path);
          392  +	if (tcl_ret != TCL_OK) {
          393  +		APPFS_DEBUG("::appfs::getattr(%s) failed.", path);
          394  +		APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp));
          395  +
          396  +		return(1);
          397  +	}
          398  +
          399  +	if (attr_key_type == NULL) {
          400  +		attr_key_type       = Tcl_NewStringObj("type", -1);
          401  +		attr_key_perms      = Tcl_NewStringObj("perms", -1);
          402  +		attr_key_size       = Tcl_NewStringObj("size", -1);
          403  +		attr_key_time       = Tcl_NewStringObj("time", -1);
          404  +		attr_key_source     = Tcl_NewStringObj("source", -1);
          405  +		attr_key_childcount = Tcl_NewStringObj("childcount", -1);
          406  +	}
          407  +
          408  +	attrs_dict = Tcl_GetObjResult(interp);
          409  +	tcl_ret = Tcl_DictObjGet(interp, attrs_dict, attr_key_type, &attr_value);
          410  +	if (tcl_ret != TCL_OK) {
          411  +		APPFS_DEBUG("[dict get \"type\"] failed");
          412  +		APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp));
          413  +
          414  +		return(1);
          415  +	}
          416  +
          417  +	if (attr_value == NULL) {
          418  +		return(1);
          419  +	}
          420  +
          421  +	pathinfo->packaged = 0;
          422  +
          423  +	attr_value_str = Tcl_GetString(attr_value);
          424  +	switch (attr_value_str[0]) {
          425  +		case 'd': /* directory */
          426  +			pathinfo->type = APPFS_PATHTYPE_DIRECTORY;
          427  +			pathinfo->typeinfo.dir.childcount = 0;
          428  +
          429  +			Tcl_DictObjGet(interp, attrs_dict, attr_key_childcount, &attr_value);
          430  +			if (attr_value != NULL) {
          431  +				tcl_ret = Tcl_GetWideIntFromObj(NULL, attr_value, &attr_value_wide);
          432  +				if (tcl_ret == TCL_OK) {
          433  +					pathinfo->typeinfo.dir.childcount = attr_value_wide;
          434  +				}
          435  +			}
          436  +
          437  +			break;
          438  +		case 'f': /* file */
          439  +			pathinfo->type = APPFS_PATHTYPE_FILE;
          440  +			pathinfo->typeinfo.file.size = 0;
          441  +			pathinfo->typeinfo.file.executable = 0;
          442  +
          443  +			Tcl_DictObjGet(interp, attrs_dict, attr_key_size, &attr_value);
          444  +			if (attr_value != NULL) {
          445  +				tcl_ret = Tcl_GetWideIntFromObj(NULL, attr_value, &attr_value_wide);
          446  +				if (tcl_ret == TCL_OK) {
          447  +					pathinfo->typeinfo.file.size = attr_value_wide;
          448  +				}
          449  +			}
          450  +
          451  +			Tcl_DictObjGet(interp, attrs_dict, attr_key_perms, &attr_value);
          452  +			if (attr_value != NULL) {
          453  +				attr_value_str = Tcl_GetString(attr_value);
          454  +				if (attr_value_str[0] == 'x') {
          455  +					pathinfo->typeinfo.file.executable = 1;
          456  +				}
          457  +			}
          458  +			break;
          459  +		case 's': /* symlink */
          460  +			pathinfo->type = APPFS_PATHTYPE_SYMLINK;
          461  +			pathinfo->typeinfo.symlink.size = 0;
          462  +			pathinfo->typeinfo.symlink.source[0] = '\0';
          463  +
          464  +			Tcl_DictObjGet(interp, attrs_dict, attr_key_source, &attr_value);
          465  +			if (attr_value != NULL) {
          466  +				attr_value_str = Tcl_GetStringFromObj(attr_value, &attr_value_int); 
          467  +
          468  +				if ((attr_value_int + 1) <= sizeof(pathinfo->typeinfo.symlink.source)) {
          469  +					pathinfo->typeinfo.symlink.size = attr_value_int;
          470  +					pathinfo->typeinfo.symlink.source[attr_value_int] = '\0';
          471  +
          472  +					memcpy(pathinfo->typeinfo.symlink.source, attr_value_str, attr_value_int);
          473  +				}
          474  +			}
          475  +			break;
          476  +		default:
          477  +			return(1);
          478  +	}
          479  +
          480  +	Tcl_DictObjGet(interp, attrs_dict, attr_key_time, &attr_value);
          481  +	if (attr_value != NULL) {
          482  +		tcl_ret = Tcl_GetWideIntFromObj(NULL, attr_value, &attr_value_wide);
          483  +		if (tcl_ret == TCL_OK) {
          484  +			pathinfo->time = attr_value_wide;
          485  +		}
          486  +	} else {
          487  +		pathinfo->time = 0;
          488  +	}
          489  +
          490  +	return(0);
   381    491   }
   382    492   
   383    493   static int appfs_fuse_readlink(const char *path, char *buf, size_t size) {
   384    494   	struct appfs_pathinfo pathinfo;
   385         -	int res = 0;
          495  +	int retval = 0;
   386    496   
   387    497   	APPFS_DEBUG("Enter (path = %s, ...)", path);
   388    498   
   389    499   	pathinfo.type = APPFS_PATHTYPE_INVALID;
   390    500   
   391         -	res = appfs_get_path_info(path, &pathinfo, NULL);
   392         -	if (res != 0) {
   393         -		return(res);
          501  +	retval = appfs_get_path_info(path, &pathinfo);
          502  +	if (retval != 0) {
          503  +		return(retval);
   394    504   	}
   395    505   
   396    506   	if (pathinfo.type != APPFS_PATHTYPE_SYMLINK) {
   397    507   		return(-EINVAL);
   398    508   	}
   399    509   
   400    510   	if ((strlen(pathinfo.typeinfo.symlink.source) + 1) > size) {
................................................................................
   404    514   	memcpy(buf, pathinfo.typeinfo.symlink.source, strlen(pathinfo.typeinfo.symlink.source) + 1);
   405    515   
   406    516   	return(0);
   407    517   }
   408    518   
   409    519   static int appfs_fuse_getattr(const char *path, struct stat *stbuf) {
   410    520   	struct appfs_pathinfo pathinfo;
   411         -	int res = 0;
          521  +	int retval;
          522  +
          523  +	retval = 0;
   412    524   
   413    525   	APPFS_DEBUG("Enter (path = %s, ...)", path);
   414    526   
   415         -	pathinfo.type = APPFS_PATHTYPE_DIRECTORY;
   416         -	pathinfo.typeinfo.dir.childcount = 0;
          527  +	pathinfo.type = APPFS_PATHTYPE_INVALID;
          528  +
          529  +	retval = appfs_get_path_info(path, &pathinfo);
          530  +	if (retval != 0) {
          531  +		return(retval);
          532  +	}
   417    533   
   418    534   	memset(stbuf, 0, sizeof(struct stat));
   419    535   
   420    536   	stbuf->st_mtime = pathinfo.time;
   421    537   	stbuf->st_ctime = pathinfo.time;
   422    538   	stbuf->st_atime = pathinfo.time;
   423    539   	stbuf->st_ino   = pathinfo.inode;
................................................................................
   440    556   			break;
   441    557   		case APPFS_PATHTYPE_SYMLINK:
   442    558   			stbuf->st_mode = S_IFLNK | 0555;
   443    559   			stbuf->st_nlink = 1;
   444    560   			stbuf->st_size = pathinfo.typeinfo.symlink.size;
   445    561   			break;
   446    562   		case APPFS_PATHTYPE_INVALID:
   447         -			res = -EIO;
          563  +			retval = -EIO;
   448    564   
   449    565   			break;
   450    566   	}
   451    567   
   452    568   	if (pathinfo.packaged) {
   453    569   		if (0) {
   454    570   			stbuf->st_mode |= 0222;
   455    571   		}
   456    572   	}
   457    573   
   458         -	return res;
          574  +	return(retval);
   459    575   }
   460    576   
   461    577   static int appfs_fuse_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
   462    578   	Tcl_Interp *interp;
   463    579   	Tcl_Obj **children;
   464    580   	int children_count, idx;
   465    581   	int tcl_ret;
................................................................................
   470    586   	interp = appfs_TclInterp();
   471    587   	if (interp == NULL) {
   472    588   		return(0);
   473    589   	}
   474    590   
   475    591   	filler(buf, ".", NULL, 0);
   476    592   	filler(buf, "..", NULL, 0);
   477         -
   478    593   
   479    594   	tcl_ret = appfs_Tcl_Eval(interp, 2, "::appfs::getchildren", path);
   480    595   	if (tcl_ret != TCL_OK) {
   481    596   		APPFS_DEBUG("::appfs::getchildren(%s) failed.", path);
   482    597   		APPFS_DEBUG("Tcl Error is: %s", Tcl_GetStringResult(interp));
   483    598   		
   484    599   		return(0);
................................................................................
   763    878   		fuse_opt_add_arg(&args, "-oallow_other");
   764    879   	}
   765    880   
   766    881   	/*
   767    882   	 * Enter the FUSE main loop -- this will process any arguments
   768    883   	 * and start servicing requests.
   769    884   	 */
          885  +	appfs_fuse_started = 1;
   770    886   	return(fuse_main(args.argc, args.argv, &appfs_operations, NULL));
   771    887   }
   772    888    

Modified appfsd.tcl from [88b17c4a86] to [c564230658].

     1      1   #! /usr/bin/env tclsh
     2      2   
     3      3   package require http 2.7
     4      4   package require sqlite3
     5      5   package require sha1
            6  +package require appfsd
     6      7   
     7      8   namespace eval ::appfs {
     8      9   	variable cachedir "/tmp/appfs-cache"
     9     10   	variable ttl 3600
    10     11   	variable nttl 60
    11     12   
    12     13   	# User-replacable function to convert a hostname/hash/method to an URL
................................................................................
   335    336   				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) );}
   336    337   				db eval {UPDATE packages SET haveManifest = 1 WHERE sha1 = $package_sha1;}
   337    338   			}
   338    339   		}
   339    340   
   340    341   		return COMPLETE
   341    342   	}
          343  +
          344  +	proc _localpath {package hostname file} {
          345  +		set homedir [::appfsd::get_homedir]
          346  +		set dir [file join $homedir .appfs "./${package}@${hostname}" "./${file}"]
          347  +	}
   342    348   
   343    349   	proc _parsepath {path} {
   344    350   		set path [string trim $path "/"]
   345    351   		set path [split $path "/"]
   346    352   		set pathlen [llength $path]
   347    353   
   348         -		array set retval [list _children sites]
          354  +		array set retval [list _children sites _type toplevel]
   349    355   
   350    356   		if {$pathlen > 0} {
   351    357   			set retval(hostname) [lindex $path 0]
   352    358   			set retval(_children) packages
          359  +			set retval(_type) sites
   353    360   
   354    361   			if {$pathlen > 1} {
   355    362   				set package [lindex $path 1]
   356    363   				if {[string length $package] == "40" && [regexp {^[a-fA-F0-9]*$} $package]} {
   357    364   					set retval(package_sha1) $package
   358    365   					set retval(_children) files
          366  +					set retval(_type) files
          367  +
          368  +					::appfs::db eval {SELECT package, os, cpuArch, version FROM packages WHERE sha1 = $retval(package_sha1);} pkginfo {}
          369  +					set retval(package) $pkginfo(package)
          370  +					set retval(os) $pkginfo(os)
          371  +					set retval(cpu) $pkginfo(cpuArch)
          372  +					set retval(version) $pkginfo(version)
   359    373   
   360    374   					if {$pathlen > 2} {
   361    375   						set retval(file) [join [lrange $path 2 end] "/"]
   362    376   					} else {
   363    377   						set retval(file) ""
   364    378   					}
   365         -
   366         -					return [array get retval]
   367    379   				} else {
   368    380   					set retval(package) $package
   369    381   					set retval(_children) os-cpu
   370         -				}
   371         -
   372         -				if {$pathlen > 2} {
   373         -					set os_cpu [lindex $path 2]
   374         -					set os_cpu [split $os_cpu "-"]
   375         -
   376         -					set retval(os) [lindex $os_cpu 0]
   377         -					set retval(cpu) [lindex $os_cpu 1]
   378         -					set retval(_children) versions
   379         -
   380         -					if {$pathlen > 3} {
   381         -						set retval(version) [lindex $path 3]
   382         -						set retval(_children) files
   383         -
   384         -						set retval(package_sha1) [::appfs::db onecolumn {SELECT sha1 FROM packages WHERE hostname = $retval(hostname) AND os = $retval(os) AND cpuArch = $retval(cpu) AND version = $retval(version);}]
   385         -						if {$retval(package_sha1) == ""} {
   386         -							return [list]
   387         -						}
   388         -
   389         -						if {$pathlen > 4} {
   390         -							set retval(file) [join [lrange $path 4 end] "/"]
   391         -						} else {
   392         -							set retval(file) ""
          382  +					set retval(_type) packages
          383  +
          384  +					if {$pathlen > 2} {
          385  +						set os_cpu [lindex $path 2]
          386  +						set os_cpu [split $os_cpu "-"]
          387  +
          388  +						set retval(os) [lindex $os_cpu 0]
          389  +						set retval(cpu) [lindex $os_cpu 1]
          390  +						set retval(_children) versions
          391  +						set retval(_type) os-cpu
          392  +
          393  +						if {$pathlen > 3} {
          394  +							set retval(version) [lindex $path 3]
          395  +							set retval(_children) files
          396  +							set retval(_type) versions
          397  +
          398  +							set retval(package_sha1) [::appfs::db onecolumn {SELECT sha1 FROM packages WHERE hostname = $retval(hostname) AND os = $retval(os) AND cpuArch = $retval(cpu) AND version = $retval(version);}]
          399  +							if {$retval(package_sha1) == ""} {
          400  +								return [list]
          401  +							}
          402  +
          403  +							if {$pathlen > 4} {
          404  +								set retval(_type) files
          405  +								set retval(file) [join [lrange $path 4 end] "/"]
          406  +							} else {
          407  +								set retval(file) ""
          408  +							}
   393    409   						}
   394    410   					}
   395    411   				}
   396    412   			}
   397    413   		}
   398    414   
   399    415   		return [array get retval]
................................................................................
   410    426   				catch {
   411    427   					::appfs::getindex $pathinfo(hostname)
   412    428   				}
   413    429   
   414    430   				return [::appfs::db eval {SELECT DISTINCT package FROM packages WHERE hostname = $pathinfo(hostname);}]
   415    431   			}
   416    432   			"os-cpu" {
   417         -				return [::appfs::db eval {SELECT DISTINCT os || "-" || cpuArch FROM packages WHERE hostname = $pathinfo(hostname) AND package = $pathinfo(package);}]
          433  +				set retval [::appfs::db eval {SELECT DISTINCT os || "-" || cpuArch FROM packages WHERE hostname = $pathinfo(hostname) AND package = $pathinfo(package);}]
          434  +
          435  +				lappend retval "platform"
          436  +
          437  +				return $retval
   418    438   			}
   419    439   			"versions" {
   420         -				return [::appfs::db eval {
          440  +				set retval [::appfs::db eval {
   421    441   					SELECT DISTINCT version FROM packages WHERE hostname = $pathinfo(hostname) AND package = $pathinfo(package) AND os = $pathinfo(os) AND cpuArch = $pathinfo(cpu);
   422    442   				}]
          443  +
          444  +				lappend retval "latest"
          445  +
          446  +				return $retval
   423    447   			}
   424    448   			"files" {
   425    449   				catch {
   426    450   					::appfs::getpkgmanifest $pathinfo(hostname) $pathinfo(package_sha1)
   427    451   				}
   428    452   
   429         -				return [::appfs::db eval {SELECT DISTINCT file_name FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_directory = $pathinfo(file);}]
          453  +				set retval [::appfs::db eval {SELECT DISTINCT file_name FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_directory = $pathinfo(file);}]
          454  +
          455  +				if {[info exists pathinfo(package)] && [info exists pathinfo(hostname)] && [info exists pathinfo(file)]} {
          456  +					set dir [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)]
          457  +					foreach file [glob -nocomplain -tails -directory $dir -types {d f l} {{.,}*}] {
          458  +						if {$file == "." || $file == ".."} {
          459  +							continue
          460  +						}
          461  +
          462  +						if {[lsearch -exact $retval $file] != -1} {
          463  +							continue
          464  +						}
          465  +
          466  +						lappend retval $file
          467  +					}
          468  +				}
          469  +
          470  +				return $retval
   430    471   			}
   431    472   		}
   432    473   
   433    474   		return -code error "Invalid or unacceptable path: $dir"
   434    475   	}
   435    476   
   436    477   	proc getattr {path} {
          478  +		array set pathinfo [_parsepath $path]
          479  +		array set retval [list]
          480  +
          481  +		switch -- $pathinfo(_type) {
          482  +			"toplevel" - "sites" - "packages" - "os-cpu" - "versions" {
          483  +				set retval(type) directory
          484  +				set retval(childcount) 2;
          485  +			}
          486  +			"files" {
          487  +				set localpath [_localpath $pathinfo(package) $pathinfo(hostname) $pathinfo(file)]
          488  +				if {[file exists $localpath]} {
          489  +					catch {
          490  +						file lstat $localpath localpathinfo
          491  +						set retval(time) $localpathinfo(mtime)
          492  +
          493  +						switch -- $localpathinfo(type) {
          494  +							"directory" {
          495  +								set retval(type) "directory"
          496  +								set retval(childcount) 2
          497  +							}
          498  +							"file" {
          499  +								set retval(type) "file"
          500  +								set retval(size) $localpathinfo(size)
          501  +								if {[file executable $localpath]} {
          502  +									set retval(perms) "x"
          503  +								} else {
          504  +									set retval(perms) ""
          505  +								}
          506  +							}
          507  +							"link" {
          508  +								set retval(type) "symlink"
          509  +								set retval(source) [file readlink $localpath]
          510  +							}
          511  +						}
          512  +					} err
          513  +				} else {
          514  +					set work [split $pathinfo(file) "/"]
          515  +					set directory [join [lrange $work 0 end-1] "/"]
          516  +					set file [lindex $work end]
          517  +					::appfs::db eval {SELECT type, time, source, size, perms FROM files WHERE package_sha1 = $pathinfo(package_sha1) AND file_directory = $directory AND file_name = $file;} retval {}
          518  +					unset -nocomplain retval(*)
          519  +				}
          520  +
          521  +			}
          522  +		}
          523  +
          524  +		if {![info exists retval(type)]} {
          525  +			return -code error "No such file or directory"
          526  +		}
          527  +
          528  +		return [array get retval]
   437    529   	}
   438    530   
   439    531   	proc openpath {path mode} {
   440    532   	}
   441    533   }