From: Steve Huntley Date: Mon, 23 Oct 2006 07:54:34 +0000 (+0000) Subject: Initial upload of template virtual filesystems in new subfolder: X-Git-Tag: vfs-1-4~49 X-Git-Url: http://privyetmir.co.uk/gitweb?a=commitdiff_plain;h=764078a2115fa6f0e546a7fe1472c3450b4141c6;p=tclvfs Initial upload of template virtual filesystems in new subfolder: * library/template/collatevfs.tcl * library/template/deltavfs.tcl * library/template/fishvfs.tcl * library/template/quotavfs.tcl * library/template/templatevfs.tcl * library/template/versionvfs.tcl Plus helper utlities: * library/template/tdelta.tcl - required by deltavfs.tcl * library/template/globfind.tcl - required by quotavfs.tcl * library/pkgIndex.tcl - added "package ifneeded" statements for template virtual filesystems. --- diff --git a/ChangeLog b/ChangeLog index afd5e89..bf95dee 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,20 @@ +2006-10-23 Steve Huntley + + Initial upload of template virtual filesystems in new subfolder: + * library/template/collatevfs.tcl + * library/template/deltavfs.tcl + * library/template/fishvfs.tcl + * library/template/quotavfs.tcl + * library/template/templatevfs.tcl + * library/template/versionvfs.tcl + + Plus helper utlities: + * library/template/tdelta.tcl - required by deltavfs.tcl + * library/template/globfind.tcl - required by quotavfs.tcl + + * library/pkgIndex.tcl - added "package ifneeded" statements for + template virtual filesystems. + 2006-09-29 Jeff Hobbs * library/httpvfs.tcl (vfs::http::geturl): wrapper around diff --git a/library/pkgIndex.tcl b/library/pkgIndex.tcl index 4931949..2c4361c 100644 --- a/library/pkgIndex.tcl +++ b/library/pkgIndex.tcl @@ -64,3 +64,17 @@ package ifneeded vfs::urltype 1.0 [list source [file join $dir vfsUrl.tcl]] package ifneeded vfs::webdav 0.1 [list source [file join $dir webdavvfs.tcl]] package ifneeded vfs::zip 1.0 [list source [file join $dir zipvfs.tcl]] package ifneeded vfs::tk 0.5 [list source [file join $dir tkvfs.tcl]] + +# Virtual filesystems based on the template vfs: +package ifneeded vfs::template::collate 1.0 [list source [file join $dir template collatevfs.tcl]] +package ifneeded vfs::template::version 1.0 [list source [file join $dir template versionvfs.tcl]] +package ifneeded vfs::template::version::delta 1.0 [list source [file join $dir template deltavfs.tcl]] +package ifneeded vfs::template::fish 1.0 [list source [file join $dir template fishvfs.tcl]] +package ifneeded vfs::template::quota 1.0 [list source [file join $dir template quotavfs.tcl]] +package ifneeded vfs::template 1.0 [list source [file join $dir template templatevfs.tcl]] + +package ifneeded globfind 1.0 [list source [file join $dir template globfind.tcl]] +package ifneeded trsync 1.0 [list source [file join $dir template tdelta.tcl]] + + + diff --git a/library/template/collatevfs.tcl b/library/template/collatevfs.tcl new file mode 100644 index 0000000..e15e5a5 --- /dev/null +++ b/library/template/collatevfs.tcl @@ -0,0 +1,357 @@ +if 0 { +######################## + +collatevfs.tcl -- + +Written by Stephen Huntley (stephen.huntley@alum.mit.edu) +License: Tcl license +Version 1.0 + +A collate/broadcast/collect/catchup virtual filesystem. Requires the template vfs in templatevfs.tcl. + +Collate: reads from multiple specified directories and presents the results as one at the mount location. + +Broadcast: applies all writes in the mount location to multiple specified directories. + +Collect: copies any file read from or written to any of the above locations to specified directories. + +Catchup: If any specified directory is not available during any write action, the action is recorded in +a catchup queue. With each subsequent write action, the queue is examined, and if any directory has +become available, the action is performed, allowing offline directories to "catch up." + +Usage: Mount ?-read -write -collect -catchup ? + +Each pathname in is meant to stand individually, the symbol is not meant to indicate a +Tcl list. The sets of specified locations are independent; they can overlap or not as desired. Note each +option flag is optional, one could for example use only the -read flag to create a read-only directory. Directories +do not have to exist and may go missing after mount, non-reachable locations will be ignored. + +Options: + +-read +When an individual file is opened for reading, each of the directories specified is searched in +order for the file; the first file found with the appropriate name is opened. When a subdirectory listing is +generated, the combined files of the corresponding subdirectory of all specified directories are listed together. + +-write +When an individual file is opened for writing, each of the directories specified is searched in +order for the file; the first file found with the appropriate name is opened. When the file is closed, a +copy of it is distributed to each specified write directory. + +-collect +Auto-generates one or more file caches; a copy of any file opened for reading or writing in any of the above +specified directories is made to each directory specified with the -collect flag. Collect locations are +not included in file or directory listings, and are not searched for read access; so in order to make an +active read cache, for example, one would have to include one directory location in both the -read and -collect sets. + +-catchup +If this flag is included, the catchup function is activated, and a copy of the catchup queue is stored in a +file in each of the specified directories. File writes, directory creations and file/directory deletes are +stored in the catchup queue if any write location is offline; at the next write/creation/delete the queue is +examined, and if any skipped action can be completed due to a location becoming available again, it +will be. A catchup attempt will be made at mount time if this flag is included. + +The values of each option can be changed dynamically after mount by using the "file attributes" command on the +mount virtual directory. Each option is editable as an attribute; i.e., "file attributes C:/collate -write C:/tmp" + +The collate vfs inherits the -cache and -volume options of the template vfs. + + +Example use: specify parallel locations on a hard drive, on a CD-ROM mount and an ftp vfs as the read list. +Files will be read first from the hard drive, if not found there the CD-ROM and ftp site will be searched in turn. +The hard drive can be specified as the single write location, and no writes to the CD-ROM or +ftp site will ever be attempted: + +Mount -read C:/install/package/docs CDROM:/package/docs FTP:/pub/releases/package/docs -write C:/install/package/docs C:/collate/docs + + +Example collect location use: specify a single hard drive location as a read and collect directory. +Specify a ftp vfs as a secondary read directory. As ftp files are downloaded they are copied to the +collect directory; the local copies are accessed first on subsequent reads: hence the collect +specification produces a self-generating local cache: + +Mount -read C:/install/package/images FTP:/pub/releases/package/images -collect C:/install/package/images C:/collate/images + + +######################## +} + +package provide vfs::template::collate 1.0 + +package require vfs::template + +namespace eval ::vfs::template::collate { + +# read template procedures into current namespace. Do not edit: +foreach templateProc [namespace eval ::vfs::template {info procs}] { + set infoArgs [info args ::vfs::template::$templateProc] + set infoBody [info body ::vfs::template::$templateProc] + proc $templateProc $infoArgs $infoBody +} + +# edit following procedures: +proc close_ {channel} { + upvar root root relative relative + foreach file [lrange [WriteFile $root $relative close] 1 end] { + set f [open $file w] + seek $channel 0 + fcopy $channel $f + close $f + } + return +} +proc file_atime {file time} { + upvar root root relative relative + set file [AcquireFile $root $relative] + file atime $file $time +} +proc file_mtime {file time} { + upvar root root relative relative + set file [AcquireFile $root $relative] + file mtime $file $time +} +proc file_attributes {file {attribute {}} args} { + upvar root root relative relative + set file [AcquireFile $root $relative] + if {($relative == {}) && ([string map {-read 1 -write 1 -collect 1 -catchup 1} $attribute] == 1)} { + set attribute [string range $attribute 1 end] + if {$args == {}} {eval return \$::vfs::template::collate::${attribute}(\$root)} + set ::vfs::template::collate::${attribute}($root) [lindex $args 0] + set ::vfs::template::collate::catchup [file isdirectory [lindex $::vfs::template::collate::catchupstore 0]] + return + } + set returnValue [eval file attributes \$file $attribute $args] + if {($relative == {}) && ($attribute == {})} {append returnValue " [list -read $::vfs::template::collate::read($root) -write $::vfs::template::collate::write($root) -collect $::vfs::template::collate::collect($root) -catchup $::vfs::template::collate::catchupstore($root)]"} + return $returnValue +} +proc file_delete {file} { + upvar root root relative relative + foreach file [WriteFile $root $relative delete] { + file delete -force -- $file + } +} +proc file_executable {file} { + upvar root root relative relative + set file [AcquireFile $root $relative] + file executable $file +} +proc file_exists {file} { + upvar root root relative relative + expr ![catch {AcquireFile $root $relative}] +} +proc file_mkdir {file} { + upvar root root relative relative + foreach file [WriteFile $root $relative mkdir] { + file mkdir $file + } +} +proc file_readable {file} { + upvar root root relative relative + set file [AcquireFile $root $relative] + file readable $file +} +proc file_stat {file array} { + upvar root root relative relative + set file [AcquireFile $root $relative] + upvar $array fs ; file stat $file fs +} +proc file_writable {file} { + upvar root root relative relative + expr ![catch {WriteFile $root $relative open}] +} +proc glob_ {directory dir nocomplain tails types typeString dashes pattern} { + upvar root root relative relative + set allFiles {} + set newFiles {} + foreach path $::vfs::template::collate::read($root) { + if ![file exists $path] {continue} + append allFiles " [glob -directory [file join $path $relative] -nocomplain -tails -types $typeString -- $pattern]" + } + set allFiles [lsort -unique $allFiles] + return $allFiles +} +proc open_ {file mode} { + upvar root root relative relative + if [string match w* $mode] { + set file [lindex [WriteFile $root $relative open] 0] + file mkdir [file dirname $file] + return [open $file $mode] + } + if [string match r* $mode] { + set file [AcquireFile $root $relative] + if {$mode == "r"} { + foreach cpath $::vfs::template::collate::collect($root) { + set cfile [file join $cpath $relative] + if {$file == $cfile} {continue} + if ![file exists $cpath] {continue} + file mkdir [::file dirname $cfile] + file copy -force -- $file $cfile + } + return [open $file r] + } + set wfile [lindex [WriteFile $root $relative open] 0] + file mkdir [file dirname $wfile] + if {$wfile != $file} {file copy -force -- $file $wfile} + return [open $wfile $mode] + } + if [string match a* $mode] { + set wfile [lindex [WriteFile $root $relative open] 0] + file mkdir [file dirname $wfile] + if ![catch {set file [AcquireFile $root $relative]}] { + if {$wfile != $file} {file copy -force -- $file $wfile} + } + return [open $wfile $mode] + } +} + +proc MountProcedure {args} { + upvar volume volume + +# take real and virtual directories from command line args. + set to [lindex $args end] + if [string equal $volume {}] {set to [::file normalize $to]} + +# add custom handling for new vfs args here. + + set ::vfs::template::collate::catchup($to) 0 + set ::vfs::template::collate::read($to) {} + set ::vfs::template::collate::write($to) {} + set ::vfs::template::collate::collect($to) {} + set ::vfs::template::collate::catchupstore($to) {} + + set args [lrange $args 0 end-1] + set argsIndex [llength $args] + for {set i 0} {$i < $argsIndex} {incr i} { + set arg [lindex $args $i] + + switch -- $arg { + -read { + set type read + } + -write { + set type write + } + -collect { + set type collect + } + -catchup { + set ::vfs::template::collate::catchup($to) 1 + set type catchupstore + } + default { + eval lappend ::vfs::template::collate::${type}(\$to) \[::file normalize \$arg\] + } + } + } + + WriteFile $to {} mkdir + +# return two-item list consisting of real and virtual locations. + lappend pathto {} + lappend pathto $to + return $pathto +} + +proc UnmountProcedure {path to} { +# add custom unmount handling of new vfs elements here. + unset -nocomplain ::vfs::template::collate::read($to) + unset -nocomplain ::vfs::template::collate::write($to) + unset -nocomplain ::vfs::template::collate::collect($to) + unset -nocomplain ::vfs::template::collate::catchup($to) + unset -nocomplain ::vfs::template::collate::catchupstore($to) + return +} + +proc AcquireFile {root relative} { + foreach path $::vfs::template::collate::read($root) { + set file [::file join $path $relative] + if [::file exists $file] { + return $file + } + } + vfs::filesystem posixerror $::vfs::posix(ENOENT) ; return -code error $::vfs::posix(ENOENT) +} + +proc WriteFile {root relative action} { + set allWriteLocations {} + foreach awl [concat $::vfs::template::collate::write($root) $::vfs::template::collate::collect($root)] { + if {[lsearch $allWriteLocations $awl] < 0} {lappend allWriteLocations $awl} + } + if ![llength $allWriteLocations] { + vfs::filesystem posixerror $::vfs::posix(EROFS) ; return -code error $::vfs::posix(EROFS) + } + if {$vfs::template::collate::catchup($root) && ([file tail $relative] != ".vfs_catchup") && ($action != "open")} { + set catchupActivate 1 + set addCatchup {} + set newCatchup {} + } else { + set catchupActivate 0 + } + set returnValue {} + foreach path $allWriteLocations { + if {$catchupActivate && ![file exists $path]} { + append addCatchup "[list $action $path $relative]\n" + continue + } + set rvfile [file join $path $relative] + if {[lsearch $returnValue $rvfile] == -1} {lappend returnValue $rvfile} + } + if {$returnValue == {}} {vfs::filesystem posixerror $::vfs::posix(EROFS) ; return -code error $::vfs::posix(EROFS)} + if $catchupActivate { + set catchup {} + set ::vfs::template::vfs_retrieve 1 + + foreach store $::vfs::template::collate::catchupstore($root) { + set store [file join $store ".vfs_catchup"] + if [file readable $store] { + set f [open $store r] + unset ::vfs::template::vfs_retrieve + seek $f 0 + set catchup [read $f] + close $f + break + } + } + catch {set currentRead [AcquireFile $root {}]} result + foreach {action path rel} $catchup { + if {$relative == $rel} {continue} + if ![file exists $path] {append newCatchup "[list $action $path $rel]\n" ; continue} + if {[lsearch $allWriteLocations $path] < 0} {continue} + switch -- $action { + close { + if {![info exists currentRead] || ([set source [file join $currentRead $rel]] == [set target [file join $path $rel]])} { + append newCatchup "[list $action $path $rel]\n" ; continue + } + if ![file exists $source] {continue} + file copy -force -- $source $target + } + delete { + file delete -force -- [file join $path $rel] + } + mkdir { + file mkdir [file join $path $rel] + } + } + } + append newCatchup $addCatchup + foreach path $::vfs::template::collate::catchupstore($root) { + set vfscatchup [file join $path ".vfs_catchup"] + set ::vfs::template::vfs_retrieve 1 + set err [catch { + if {$newCatchup != {}} { + set f [open $vfscatchup w] + puts $f $newCatchup + close $f + } else { + file delete $vfscatchup + } + } result] + unset ::vfs::template::vfs_retrieve + } + } + return $returnValue +} + +} +# end namespace ::vfs::template::collate + diff --git a/library/template/deltavfs.tcl b/library/template/deltavfs.tcl new file mode 100644 index 0000000..9c2c024 --- /dev/null +++ b/library/template/deltavfs.tcl @@ -0,0 +1,284 @@ +if 0 { +######################## + +deltavfs.tcl -- + +Written by Stephen Huntley (stephen.huntley@alum.mit.edu) +License: Tcl license +Version 1.0 + +A delta virtual filesystem. Requires the template vfs in templatevfs.tcl. + +Mount the delta vfs first, then mount the versioning vfs using the virtual location created by the +delta vfs as its existing directory. + +As the versioning filesystem generates a new separate file for every file edit, this filesystem will +invisibly generate and manage deltas of the separate versions to save space. + + +Usage: Mount + + +The delta vfs inherits the -cache and -volume options of the template vfs. + +######################## +} + +package provide vfs::template::version::delta 1.0 + +package require vfs::template + +namespace eval ::vfs::template::version::delta { + +# read template procedures into current namespace. Do not edit: +foreach templateProc [namespace eval ::vfs::template {info procs}] { + set infoArgs [info args ::vfs::template::$templateProc] + set infoBody [info body ::vfs::template::$templateProc] + proc $templateProc $infoArgs $infoBody +} + +# edit following procedures: +proc close_ {channel} { + upvar path path relative relative + set file [file join $path $relative] + set fileName $file + set f [open $fileName w] + fconfigure $f -translation binary + seek $f 0 + seek $channel 0 + fcopy $channel $f + close $f + Delta $fileName + return +} +proc file_atime {file time} { + set file [GetFileName $file] + file atime $file $time +} +proc file_mtime {file time} { + set file [GetFileName $file] + file mtime $file $time +} +proc file_attributes {file {attribute {}} args} { + set file [GetFileName $file] + eval file attributes \$file $attribute $args +} +proc file_delete {file} { + if [file isdirectory $file] {catch {file delete $file}} + + set fileName [GetFileName $file] + set timeStamp [lindex [split [file tail $fileName] \;] 1] + if [string equal $timeStamp {}] { + catch {file delete $fileName} result + return + } + set targetFile [Reconstitute $fileName] + set referenceFiles [glob -directory [file dirname $fileName] -nocomplain *vfs&delta$timeStamp] + if {[lindex [file system $fileName] 0] != "tclvfs"} {append referenceFiles " [glob -directory [file dirname $fileName] -nocomplain -type hidden *vfs&delta$timeStamp]"} + foreach referenceFile $referenceFiles { + regsub {\;vfs&delta[0-9]*$} $referenceFile "" reconFile] + set f [open $referenceFile r] + fconfigure $f -translation binary + set signature [read $f] + close $f + tpatch $targetFile $signature $reconFile + file delete $referenceFile + } + close $targetFile + + file delete -force -- $fileName +} +proc file_executable {file} { + set file [GetFileName $file] + file executable $file +} +proc file_exists {file} { + set file [GetFileName $file] + file exists $file +} +proc file_mkdir {file} {file mkdir $file} +proc file_readable {file} { + set file [GetFileName $file] + file readable $file +} +proc file_stat {file array} { + upvar $array fs + set fileName [GetFileName $file] + + set endtag [lindex [split $fileName \;] end] + if {[string first "vfs&delta" $endtag] || [string equal "vfs&delta" $endtag]} {file stat $fileName fs ; return} + set f [open $fileName r] + fconfigure $f -translation binary + set copyinstructions [read $f] + close $f + array set fileStats [lindex $copyinstructions 3] + unset copyinstructions + set size $fileStats(size) + file stat $fileName fs + set fs(size) $size + return +} +proc file_writable {file} { + set file [GetFileName $file] + file writable $file +} +proc glob_ {directory dir nocomplain tails types typeString dashes pattern} { + set globList [glob -directory $dir -nocomplain -tails -types $typeString -- $pattern] + set newGlobList {} + foreach gL $globList { + regsub {\;vfs&delta.*$} $gL "" gL + lappend newGlobList $gL + } + return $newGlobList +} +proc open_ {file mode} { + set fileName [GetFileName $file] + + set newFile 0 + if ![file exists $fileName] {set newFile 1} + set fileName $file + set channelID [Reconstitute $fileName] + if [string equal $channelID {}] {set channelID [open $fileName $mode] ; close $channelID ; set channelID [::vfs::memchan]} + if $newFile {catch {file attributes $fileName -permissions $permissions}} + return $channelID +} + + +proc MountProcedure {args} { + upvar volume volume + +# take real and virtual directories from command line args. + set to [lindex $args end] + if [string equal $volume {}] {set to [::file normalize $to]} + set path [::file normalize [lindex $args end-1]] + +# make sure mount location exists: + ::file mkdir $path + +# add custom handling for new vfs args here. + package require trsync + namespace import -force ::trsync::tdelta ::trsync::tpatch + +# return two-item list consisting of real and virtual locations. + lappend pathto $path + lappend pathto $to + return $pathto +} + + +proc UnmountProcedure {path to} { +# add custom unmount handling of new vfs elements here. + + return +} + +proc Delta {filename} { + set fileRoot [lindex [split [file tail $filename] \;] 0] + set fileNames [glob -nocomplain -path [file join [file dirname $filename] $fileRoot] *] + if {[lindex [file system $filename] 0] != "tclvfs"} {append fileNames " [glob -nocomplain -path [file join [file dirname $filename] $fileRoot] -type hidden *]"} + set nonDeltas {} + foreach fn $fileNames { + set endtag [lindex [split $fn \;] end] + if ![string first "vfs&delta" $endtag] {continue} + lappend nonDeltas $fn + set atimes($fn) [file atime $fn] + } + if {[set deltaIndex [llength $nonDeltas]] < 2} {return} + set nonDeltas [lsort -dictionary $nonDeltas] + incr deltaIndex -1 + set i 0 + while {$i < $deltaIndex} { + set referenceFile [lindex $nonDeltas $i] + set targetFile [lindex $nonDeltas [incr i]] + set signature [tdelta $referenceFile $targetFile $::trsync::blockSize 1 1] + set targetTimeStamp [lindex [split $targetFile \;] 1] + + file stat $referenceFile fileStats + set signatureSize [string length $signature] + if {$signatureSize > $fileStats(size)} { + set fileName $referenceFile\;vfs&delta + file rename $referenceFile $fileName + continue + } + + array set fileStats [file attributes $referenceFile] + + set fileName $referenceFile\;vfs&delta$targetTimeStamp + set f [open $fileName w] + fconfigure $f -translation binary + puts -nonewline $f $signature + close $f + file delete $referenceFile + array set fileAttributes [file attributes $fileName] + if [info exists fileAttributes(-readonly)] {catch {file attributes $fileName -readonly 0}} + if [info exists fileAttributes(-permissions)] {catch {file attributes $fileName -permissions rw-rw-rw-}} + catch {file attributes $fileName -owner $fileStats(uid)} + catch {file attributes $fileName -group $fileStats(gid)} + + catch {file mtime $fileName $fileStats(mtime)} + catch {file atime $fileName $fileStats(atime)} + + foreach attr [array names fileStats] { + if [string first "-" $attr] {continue} + if [string equal [array get fileStats $attr] [array get fileAttributes $attr]] {continue} + if [string equal "-permissions" $attr] {continue} + catch {file attributes $fileName $attr $fileStats($attr)} + } + catch {file attributes $fileName -permissions $fileStats(mode)} + catch {file attributes $fileName -readonly $fileStats(-readonly)} + } + foreach fn [array names atimes] { + if ![file exists $fn] {continue} + file atime $fn $atimes($fn) + } +} + +proc GetFileName {file} { + set fileNames [glob -nocomplain -path $file *] + if {[lindex [file system $file] 0] != "tclvfs"} {append fileNames " [glob -nocomplain -path $file -type hidden *]"} + set fileName [lindex $fileNames 0] + if [set i [expr [lsearch -exact $fileNames $file] + 1]] {set fileName [lindex $fileNames [incr i -1]]} + return $fileName +} + +proc Reconstitute {fileName} { + if ![catch {set channelID [open $fileName r]}] {return $channelID} + if ![catch {set channelID [open $fileName\;vfs&delta r]}] {return $channelID} + set targetFiles [glob -nocomplain -path $fileName *] + if {[lindex [file system $fileName] 0] != "tclvfs"} {append targetFiles " [glob -nocomplain -path $fileName -type hidden *]"} + set targetFile [lindex $targetFiles 0] + + set targetFile [string trim $targetFile] + if [string equal $targetFile {}] {return} + set fileStack {} + while {[string first "\;vfs&delta" $targetFile] > -1} { + if ![regexp {\;vfs&delta([0-9]+)$} $targetFile trash targetTime] {break} + set fileStack "[list $targetFile] $fileStack" + set targetFiles [glob -directory [file dirname $fileName] *\;$targetTime*] + if {[lindex [file system $fileName] 0] != "tclvfs"} {append targetFiles " [glob -directory [file dirname $fileName] -nocomplain -type hidden *\;$targetTime*]"} + set targetFile [lindex $targetFiles 0] + + set atimes($targetFile) [file atime $targetFile] + } + set targetFile [open $targetFile r] + foreach fs $fileStack { + set f [open $fs r] + fconfigure $f -translation binary + set copyInstructions [read $f] + close $f + set fileToConstruct [::vfs::memchan] + tpatch $targetFile $copyInstructions $fileToConstruct + catch {close $targetFile} + set targetFile $fileToConstruct + } + foreach fn [array names atimes] { + file atime $fn $atimes($fn) + } + fconfigure $targetFile -translation auto + seek $targetFile 0 + return $targetFile +} + +} +# end namespace ::vfs::template::version::delta + diff --git a/library/template/fishvfs.tcl b/library/template/fishvfs.tcl new file mode 100644 index 0000000..fd10b71 --- /dev/null +++ b/library/template/fishvfs.tcl @@ -0,0 +1,536 @@ +#! /usr/bin/env tclsh + +if 0 { +######################## + +fishvfs.tcl -- + + A "FIles transferred over SHell" virtual filesystem + This is not an official "FISH" protocol client as described at: + http://mini.net/tcl/12792 + but it utilizes the same concept of turning any computer that offers + access via ssh, rsh or similar shell into a file server. + + Written by Stephen Huntley (stephen.huntley@alum.mit.edu) + License: Tcl license + Version 1.0 + + Usage: Mount ?-volume? \ + ?-cache ? \ # cache retention seconds + ?-exec? \ # location of executable + ?-transport ? \ # can be ssh, rsh or plink + ?-user ? \ # remote computer login name + ?-password ? \ # remote computer login password + ?-host ? \ # remote computer domain name + ?-port ? \ # override default port + ?