ADDED kitdll/buildsrc/kitdll-0.0/Makefile Index: kitdll/buildsrc/kitdll-0.0/Makefile ================================================================== --- kitdll/buildsrc/kitdll-0.0/Makefile +++ kitdll/buildsrc/kitdll-0.0/Makefile @@ -0,0 +1,23 @@ +CC = gcc +CFLAGS = -Wall -O3 +CPPFLAGS = -DUSE_TCL_STUBS +LIBS = -ltclstub8.5 + +all: vfs_kitdll_data.so + +vfs_kitdll_data.h: dir2c.tcl starpack.vfs + ./dir2c.tcl vfs_kitdll_data starpack.vfs > vfs_kitdll_data.h + +vfs_kitdll.tcl.h: vfs_kitdll.tcl + ./stringify.tcl vfs_kitdll.tcl > vfs_kitdll.tcl.h + +vfs_kitdll_data.o: vfs_kitdll_data.c vfs_kitdll_data.h vfs_kitdll.tcl.h + +vfs_kitdll_data.so: vfs_kitdll_data.o + $(CC) $(CPPFLAGS) $(CFLAGS) -rdynamic -shared $(LDFLAGS) -o vfs_kitdll_data.so vfs_kitdll_data.o $(LIBS) + +clean: + rm -f vfs_kitdll_data.so vfs_kitdll_data.o + rm -f vfs_kitdll.tcl.h vfs_kitdll_data.h + +.PHONY: clean DELETED kitdll/buildsrc/kitdll-0.0/cvfs.tcl Index: kitdll/buildsrc/kitdll-0.0/cvfs.tcl ================================================================== --- kitdll/buildsrc/kitdll-0.0/cvfs.tcl +++ kitdll/buildsrc/kitdll-0.0/cvfs.tcl @@ -1,348 +0,0 @@ -#! /usr/bin/env tcl - -package require vfs -#package require kitdll - -namespace eval ::vfs::kitdll {} - -# Convience functions -proc ::vfs::kitdll::Mount {hashkey local} { - vfs::filesystem mount $local [list ::vfs::kitdll::vfshandler $hashkey] - vfs::RegisterMount $local [list ::vfs::kitdll::Unmount] -} - -proc ::vfs::kitdll::Unmount {local} { - vfs::filesystem unmount $local -} - -# Implementation - -## Filesystem Data -namespace eval ::vfs::kitdll::data {} -set ::vfs::kitdll::data(joe) "Test\n" -set {::vfs::kitdll::metadata()} [list type directory ino 0 mode 0555 nlink 2 uid 0 gid 0 size 0 atime 0 mtime 0 ctime 0] -set ::vfs::kitdll::metadata(joe) [list type file ino 1 mode 0444 nlink 1 uid 0 gid 0 size 5 atime 0 mtime 0 ctime 0] -set ::vfs::kitdll::metadata(sub) [list type directory ino 2 mode 0555 nlink 1 uid 0 gid 0 size 0 atime 0 mtime 0 ctime 0] -set ::vfs::kitdll::metadata(sub/sub2) [list type directory ino 3 mode 0555 nlink 1 uid 0 gid 0 size 0 atime 0 mtime 0 ctime 0] - -proc ::vfs::kitdll::data::getData {hashkey file {start 0} {end "end"}} { - if {![info exists ::vfs::kitdll::data($file)]} { - return -code error "Invalid operation" - } - - return [string range $::vfs::kitdll::data($file) $start $end] -} - -proc ::vfs::kitdll::data::getMetadata {hashkey file} { - if {![info exists ::vfs::kitdll::metadata($file)]} { - return -code error "No such file" - } - - return $::vfs::kitdll::metadata($file) -} - -proc ::vfs::kitdll::data::getChildren {hashkey directory} { - set pattern [file join $directory {[^/]*}] - - set children [array names ::vfs::kitdll::metadata -regexp "^${pattern}\$"] - - set newchildren [list] - foreach child $children { - if {$child == ""} { - continue - } - - set tail [lindex [split $child /] end] - - lappend newchildren $tail - } - - return $newchildren -} - -## VFS and Chan I/O -### Dispatchers -proc ::vfs::kitdll::vfshandler {hashkey subcmd args} { - set cmd $args - set cmd [linsert $cmd 0 "::vfs::kitdll::vfsop_${subcmd}" $hashkey] - - return [eval $cmd] -} - -proc ::vfs::kitdll::chanhandler {hashkey subcmd args} { - set cmd $args - set cmd [linsert $cmd 0 "::vfs::kitdll::chanop_${subcmd}" $hashkey] - - return [eval $cmd] -} - -### Actual handlers -#### Channel operation handlers -proc ::vfs::kitdll::chanop_initialize {hashkey chanId mode} { - return [list initialize finalize watch read seek] -} - -proc ::vfs::kitdll::chanop_finalize {hashkey chanId} { - unset -nocomplain ::vfs::kitdll::chandata([list $hashkey $chanId]) - - return -} - -proc ::vfs::kitdll::chanop_watch {hashkey chanId eventSpec} { - array set chaninfo $::vfs::kitdll::chandata([list $hashkey $chanId]) - - set chaninfo(watching) $eventSpec - - set ::vfs::kitdll::chandata([list $hashkey $chanId]) [array get chaninfo] - - if {[lsearch -exact $chaninfo(watching) "read"] != -1} { - after 0 [list catch "chan postevent $chanId [list {read}]"] - } - - return -} - -proc ::vfs::kitdll::chanop_read {hashkey chanId bytes} { - array set chaninfo $::vfs::kitdll::chandata([list $hashkey $chanId]) - - set pos $chaninfo(pos) - set len $chaninfo(len) - - if {[lsearch -exact $chaninfo(watching) "read"] != -1} { - after 0 [list catch "chan postevent $chanId [list {read}]"] - } - - if {$pos == $len} { - return "" - } - - set end [expr {$pos + $bytes}] - if {$end > $len} { - set end $len - } - - set data [::vfs::kitdll::data::getData $hashkey $chaninfo(file) $pos $end] - - set dataLen [string length $data] - incr pos $dataLen - - set chaninfo(pos) $pos - - set ::vfs::kitdll::chandata([list $hashkey $chanId]) [array get chaninfo] - - return $data -} - -proc ::vfs::kitdll::chanop_seek {hashkey chanId offset origin} { - array set chaninfo $::vfs::kitdll::chandata([list $hashkey $chanId]) - - set pos $chaninfo(pos) - set len $chaninfo(len) - - switch -- $origin { - "start" - "0" { - set pos $offset - } - "current" - "1" { - set pos [expr {$pos + $offset}] - } - "end" - "2" { - set pos [expr {$len + $offset}] - } - } - - if {$pos < 0} { - set pos 0 - } - - if {$pos > $len} { - set pos $len - } - - set chaninfo(pos) $pos - set ::vfs::kitdll::chandata([list $hashkey $chanId]) [array get chaninfo] - - return $pos -} - -#### VFS operation handlers -proc ::vfs::kitdll::vfsop_stat {hashkey root relative actualpath} { - catch { - set ret [::vfs::kitdll::data::getMetadata $hashkey $relative] - } - - if {![info exists ret]} { - vfs::filesystem posixerror $::vfs::posix(ENOENT) - } - - return $ret -} - -proc ::vfs::kitdll::vfsop_access {hashkey root relative actualpath mode} { - set ret [::vfs::kitdll::data::getMetadata $hashkey $relative] - - if {$mode & 0x2} { - vfs::filesystem posixerror $::vfs::posix(EROFS) - } - - return 1 -} - -proc ::vfs::kitdll::vfsop_matchindirectory {hashkey root relative actualpath pattern types} { - set ret [list] - - catch { - array set metadata [::vfs::kitdll::data::getMetadata $hashkey $relative] - } - - if {![info exists metadata]} { - return [list] - } - - if {$pattern == ""} { - - set children [list $relative] - } else { - set children [::vfs::kitdll::data::getChildren $hashkey $relative] - } - - foreach child $children { - if {![string match $pattern $child]} { - continue - } - - unset -nocomplain metadata - catch { - array set metadata [::vfs::kitdll::data::getMetadata $hashkey $child] - } - - if {[string index $actualpath end] == "/"} { - set child "${actualpath}${child}" - } else { - set child "${actualpath}/${child}" - } - if {[string index $child end] == "/"} { - set child [string range $child 0 end-1] - } - - if {![info exists metadata(type)]} { - continue - } - - set filetype 0 - switch -- $metadata(type) { - "directory" { - set filetype [expr {$filetype | 0x04}] - } - "file" { - set filetype [expr {$filetype | 0x10}] - } - "link" { - set filetype [expr {$filetype | 0x20}] - } - default { - continue - } - } - - if {($filetype & $types) != $types} { - continue - } - - lappend ret $child - } - - return $ret -} - -proc ::vfs::kitdll::vfsop_fileattributes {hashkey root relative actualpath {index -1} {value ""}} { - set attrs [list -owner -group -permissions] - - if {$value != ""} { - vfs::filesystem posixerror $::vfs::posix(EROFS) - } - - if {$index == -1} { - return $attrs - } - - array set metadata [::vfs::kitdll::data::getMetadata $hashkey $relative] - - set attr [lindex $attrs $index] - - switch -- $attr { - "-owner" { - return $metadata(uid) - } - "-group" { - return $metadata(gid) - } - "-permissions" { - if {$metadata(type) == "directory"} { - set metadata(mode) [expr {$metadata(mode) | 040000}] - } - - return [format {0%o} $metadata(mode)] - } - } - - return -code error "Invalid index" -} - -proc ::vfs::kitdll::vfsop_open {hashkey root relative actualpath mode permissions} { - if {$mode != "" && $mode != "r"} { - vfs::filesystem posixerror $::vfs::posix(EROFS) - } - - catch { - array set metadata [::vfs::kitdll::data::getMetadata $hashkey $relative] - } - - if {![info exists metadata]} { - vfs::filesystem posixerror $::vfs::posix(ENOENT) - } - - if {$metadata(type) == "directory"} { - vfs::filesystem posixerror $::vfs::posix(EISDIR) - } - - if {[info command chan] != ""} { - set chan [chan create [list "read"] [list ::vfs::kitdll::chanhandler $hashkey]] - - set ::vfs::kitdll::chandata([list $hashkey $chan]) [list file $relative pos 0 len $metadata(size) watching ""] - - return [list $chan] - } - - if {[info command rechan] == ""} { - catch { - package require rechan - } - } - - if {[info command rechan] != ""} { - set chan [rechan [list ::vfs::kitdll::chanhandler $hashkey] 2] - - set ::vfs::kitdll::chandata([list $hashkey $chan]) [list file $relative pos 0 len $metadata(size) watching ""] - - return [list $chan] - } - - return -code error "No way to generate a channel, need either Tcl 8.5+, \"rechan\"" -} - -##### No-Ops since we are a readonly filesystem -proc ::vfs::kitdll::vfsop_createdirectory {args} { - vfs::filesystem posixerror $::vfs::posix(EROFS) -} -proc ::vfs::kitdll::vfsop_deletefile {args} { - vfs::filesystem posixerror $::vfs::posix(EROFS) -} -proc ::vfs::kitdll::vfsop_removedirectory {args} { - vfs::filesystem posixerror $::vfs::posix(EROFS) -} -proc ::vfs::kitdll::vfsop_utime {} { - vfs::filesystem posixerror $::vfs::posix(EROFS) -} - -package provide vfs::kitdll 1.0 Index: kitdll/buildsrc/kitdll-0.0/dir2c.tcl ================================================================== --- kitdll/buildsrc/kitdll-0.0/dir2c.tcl +++ kitdll/buildsrc/kitdll-0.0/dir2c.tcl @@ -1,9 +1,9 @@ #! /usr/bin/env tclsh if {[llength $argv] != 2} { - puts stderr "Usage: dir2c " + puts stderr "Usage: kitdll " exit 1 } set hashkey [lindex $argv 0] @@ -23,11 +23,11 @@ } proc recursive_glob {dir} { set children [glob -nocomplain -directory $dir *] - set ret [list] + set ret [list $dir] foreach child $children { unset -nocomplain childinfo catch { file stat $child childinfo } @@ -39,12 +39,10 @@ if {$childinfo(type) == "directory"} { foreach add [recursive_glob $child] { lappend ret $add } - lappend ret $child - continue } if {$childinfo(type) != "file"} { continue @@ -54,30 +52,12 @@ } return $ret } -proc dir2c_hash {path} { - set h 0 - set g 0 - - for {set idx 0} {$idx < [string length $path]} {incr idx} { - binary scan [string index $path $idx] H* char - set char "0x$char" - - set h [expr {($h << 4) + $char}] - set g [expr {$h & 0xf0000000}] - if {$g != 0} { - set h [expr {($h & 0xffffffff) ^ ($g >> 24)}] - } - - set h [expr {$h & ((~$g) & 0xffffffff)}] - } - - return $h -} - +# Convert a string into a C-style binary string +## XXX: This function needs to be optimized proc stringify {data} { set ret "\"" for {set idx 0} {$idx < [string length $data]} {incr idx} { binary scan [string index $data $idx] H* char @@ -93,36 +73,76 @@ set ret "\"$ret\"" return $ret } +# This function must be kept in-sync with the generated C function below +proc kitdll_hash {path} { + set h 0 + set g 0 + + for {set idx 0} {$idx < [string length $path]} {incr idx} { + binary scan [string index $path $idx] H* char + set char "0x$char" + + set h [expr {($h << 4) + $char}] + set g [expr {$h & 0xf0000000}] + if {$g != 0} { + set h [expr {($h & 0xffffffff) ^ ($g >> 24)}] + } + + set h [expr {$h & ((~$g) & 0xffffffff)}] + } + + return $h +} + +# Generate list of files to include in output set files [recursive_glob $startdir] -set cpp_tag "DIR2C_[string toupper $hashkey]" -set code_tag "dir2c_[string tolower $hashkey]" +# Insert dummy entry cooresponding to C dummy entry +set files [linsert $files 0 "__DUMMY__"] + +# Produce C89 compatible header +set cpp_tag "KITDLL_[string toupper $hashkey]" +set code_tag "kitdll_[string tolower $hashkey]" puts "#ifndef $cpp_tag" puts "# define $cpp_tag 1" -puts {# include +puts { +# ifdef HAVE_STDC +# ifndef HAVE_UNISTD_H +# define HAVE_UNISTD_H 1 +# endif +# ifndef HAVE_STRING_H +# define HAVE_STRING_H 1 +# endif +# endif +# ifdef HAVE_UNISTD_H +# include +# endif +# ifdef HAVE_STRING_H +# include +# endif -# ifndef LOADED_DIR2C_COMMON -# define LOADED_DIR2C_COMMON 1 +# ifndef LOADED_KITDLL_COMMON +# define LOADED_KITDLL_COMMON 1 typedef enum { - DIR2C_FILETYPE_FILE, - DIR2C_FILETYPE_DIR -} dir2c_filetype_t; - -struct dir2c_data { - const char *name; - unsigned long index; - unsigned long size; - dir2c_filetype_t type; - const unsigned char *data; + KITDLL_FILETYPE_FILE, + KITDLL_FILETYPE_DIR +} kitdll_filetype_t; + +struct kitdll_data { + const char * name; + unsigned long index; + unsigned long size; + kitdll_filetype_t type; + const unsigned char * data; }; -static unsigned long dir2c_hash(const unsigned char *path) { +static unsigned long kitdll_hash(const unsigned char *path) { unsigned long i, h = 0, g = 0; for (i = 0; path[i]; i++) { h = (h << 4) + path[i]; g = h & 0xf0000000; @@ -133,101 +153,201 @@ } return(h); } -# endif /* !LOADED_DIR2C_COMMON */} +# endif /* !LOADED_KITDLL_COMMON */} puts "" -puts "static struct dir2c_data ${code_tag}_data\[\] = {" +puts "static struct kitdll_data ${code_tag}_data\[\] = {" puts "\t{" puts "\t\t.name = NULL," puts "\t\t.index = 0," puts "\t\t.type = 0," puts "\t\t.size = 0," puts "\t\t.data = NULL," puts "\t}," -puts "\t{" -puts "\t\t.name = \"\"," -puts "\t\t.index = 1," -puts "\t\t.type = DIR2C_FILETYPE_DIR," -puts "\t\t.size = 0," -puts "\t\t.data = NULL," -puts "\t}," -for {set idx 0} {$idx < [llength $files]} {incr idx} { +for {set idx 1} {$idx < [llength $files]} {incr idx} { set file [lindex $files $idx] set shortfile [shorten_file $startdir $file] unset -nocomplain finfo type file stat $file finfo switch -- $finfo(type) { "file" { - set type "DIR2C_FILETYPE_FILE" + set type "KITDLL_FILETYPE_FILE" set size $finfo(size) set fd [open $file] fconfigure $fd -translation binary set data [read $fd] close $fd - set data [stringify $data] + set data "(unsigned char *) [stringify $data]" } "directory" { - set type "DIR2C_FILETYPE_DIR" + set type "KITDLL_FILETYPE_DIR" set data "NULL" set size 0 } } puts "\t{" puts "\t\t.name = \"$shortfile\"," - puts "\t\t.index = [expr $idx + 2]," + puts "\t\t.index = $idx," puts "\t\t.type = $type," puts "\t\t.size = $size," puts "\t\t.data = $data," puts "\t}," } puts "};" puts "" puts "static unsigned long ${code_tag}_lookup_index(const char *path) {" -puts "\tswitch (dir2c_hash(path)) {" -puts "\t\tcase [dir2c_hash {}]:" -puts "\t\t\treturn(1);" +puts "\tswitch (kitdll_hash((unsigned char *) path)) {" -set seenhashes [list] -for {set idx 0} {$idx < [llength $files]} {incr idx} { +for {set idx 1} {$idx < [llength $files]} {incr idx} { set file [lindex $files $idx] set shortfile [shorten_file $startdir $file] - set hash [dir2c_hash $shortfile] - - if {[lsearch -exact $seenhashes $hash] != -1} { - puts stderr "ERROR: Duplicate hash seen: $file ($hash), aborting" - - exit 1 - } - - lappend seenhashes $hash - + set hash [kitdll_hash $shortfile] + + lappend indexes_per_hash($hash) [list $shortfile $idx] +} + +foreach {hash idx_list} [array get indexes_per_hash] { puts "\t\tcase $hash:" - puts "\t\t\treturn([expr $idx + 2]);" + + if {[llength $idx_list] == 1} { + set idx [lindex $idx_list 0 1] + + puts "\t\t\treturn($idx);" + } else { + foreach idx_ent $idx_list { + set shortfile [lindex $idx_ent 0] + set idx [lindex $idx_ent 1] + + puts "\t\t\tif (strcmp(path, \"$shortfile\") == 0) return($idx);" + } + puts "\t\t\tbreak;" + } } puts "\t}" puts "\treturn(0);" puts "}" puts "" -puts "static struct dir2c_data *${code_tag}_getData(const char *path) {" -puts "\tunsigned long index;" +puts "static struct kitdll_data *${code_tag}_getData(const char *path, unsigned long index) {" +puts "\tif (path != NULL) {" +puts "\t\tindex = ${code_tag}_lookup_index(path);" +puts "\t}" puts "" -puts "\tindex = ${code_tag}_lookup_index(path);" puts "\tif (index == 0) {" puts "\t\treturn(NULL);" puts "\t}" +puts "" +puts "\tif (path != NULL) {" +puts "\t\tif (strcmp(path, ${code_tag}_data\[index\].name) != 0) {" +puts "\t\t\treturn(NULL);" +puts "\t\t}" +puts "\t}" puts "" puts "\treturn(&${code_tag}_data\[index\]);" puts "}" puts "" + +puts "static unsigned long ${code_tag}_getChildren(const char *path, unsigned long *outbuf, unsigned long outbuf_count) {" +puts "\tunsigned long index;" +puts "\tunsigned long num_children = 0;" +puts "" +puts "\tindex = ${code_tag}_lookup_index(path);" +puts "\tif (index == 0) {" +puts "\t\treturn(0);" +puts "\t}" +puts "" +puts "\tif (${code_tag}_data\[index\].type != KITDLL_FILETYPE_DIR) {" +puts "\t\treturn(0);" +puts "\t}" +puts "" +puts "\tif (strcmp(path, ${code_tag}_data\[index\].name) != 0) {" +puts "\t\treturn(0);" +puts "\t}" +puts "" +puts "\tswitch (index) {" + +unset -nocomplain children +array set children [list] +for {set idx 1} {$idx < [llength $files]} {incr idx} { + set file [lindex $files $idx] + set shortfile [shorten_file $startdir $file] + + unset -nocomplain finfo type + file stat $file finfo + + if {$finfo(type) != "directory"} { + continue; + } + + # Determine which children are under this directory + ## Convert the current pathname to a regular expression that matches exactly + set file_regexp [string map [list "\\" "\\\\" "." "\\." "\{" "\\\{" "\}" "\\\}" "*" "\\*"] $file] + + ## Search for pathnames which start with exactly our name, followed by a slash + ## followed by no more slashes (direct descendants) + set child_idx_list [lsearch -regexp -all $files "^${file_regexp}/\[^/\]*$"] + + set children($idx) $child_idx_list + + puts "\t\tcase $idx:" + puts "\t\t\tnum_children = [llength $child_idx_list];" + puts "\t\t\tbreak;" + +} + +puts "\t}" +puts "" +puts "\tif (outbuf == NULL) {" +puts "\t\treturn(num_children);" +puts "\t}" +puts "" +puts "\tif (num_children > outbuf_count) {" +puts "\t\tnum_children = outbuf_count;" +puts "\t}" +puts "" +puts "\tif (num_children == 0) {" +puts "\t\treturn(num_children);" +puts "\t}" +puts "" +puts "\tif (outbuf_count > num_children) {" +puts "\t\toutbuf_count = num_children;" +puts "\t}" +puts "" +puts "\tswitch (index) {" + +foreach {idx child_idx_list} [array get children] { + if {[llength $child_idx_list] == 0} { + continue + } + + puts "\t\tcase $idx:" + puts "\t\t\tswitch(outbuf_count) {" + + for {set child_idx_idx [expr {[llength $child_idx_list] - 1}]} {$child_idx_idx >= 0} {incr child_idx_idx -1} { + set child_idx [lindex $child_idx_list $child_idx_idx] + + puts "\t\t\t\tcase [expr {$child_idx_idx + 1}]:" + puts "\t\t\t\t\toutbuf\[$child_idx_idx\] = $child_idx;" + } + + puts "\t\t\t}" + + puts "\t\t\tbreak;" +} + +puts "\t}" +puts "" +puts "\treturn(num_children);" +puts "}" +puts "" puts "#endif /* !$cpp_tag */" ADDED kitdll/buildsrc/kitdll-0.0/stringify.tcl Index: kitdll/buildsrc/kitdll-0.0/stringify.tcl ================================================================== --- kitdll/buildsrc/kitdll-0.0/stringify.tcl +++ kitdll/buildsrc/kitdll-0.0/stringify.tcl @@ -0,0 +1,27 @@ +#! /usr/bin/env tclsh + +proc stringifyfile {filename {key 0}} { + catch { + set fd [open $filename r] + } + + if {![info exists fd]} { + return "" + } + + set data [read -nonewline $fd] + close $fd + + foreach line [split $data \n] { + set line [string map [list "\\" "\\\\" "\"" "\\\""] $line] + append ret " \"$line\\n\"\n" + } + + return $ret +} + +foreach file $argv { + puts -nonewline [stringifyfile $file] +} + +exit 0 ADDED kitdll/buildsrc/kitdll-0.0/vfs_kitdll.tcl Index: kitdll/buildsrc/kitdll-0.0/vfs_kitdll.tcl ================================================================== --- kitdll/buildsrc/kitdll-0.0/vfs_kitdll.tcl +++ kitdll/buildsrc/kitdll-0.0/vfs_kitdll.tcl @@ -0,0 +1,316 @@ +#! /usr/bin/env tcl + +package require vfs +#package require kitdll + +namespace eval ::vfs::kitdll {} + +# Convience functions +proc ::vfs::kitdll::Mount {hashkey local} { + vfs::filesystem mount $local [list ::vfs::kitdll::vfshandler $hashkey] + vfs::RegisterMount $local [list ::vfs::kitdll::Unmount] +} + +proc ::vfs::kitdll::Unmount {local} { + vfs::filesystem unmount $local +} + +# Implementation + +## Filesystem Data +proc ::vfs::kitdll::data::getData {hashkey file {start 0} {end "end"}} { + if {![info exists ::vfs::kitdll::data($file)]} { + return -code error "Invalid operation" + } + + return [string range $::vfs::kitdll::data($file) $start $end] +} + +## VFS and Chan I/O +### Dispatchers +proc ::vfs::kitdll::vfshandler {hashkey subcmd args} { + set cmd $args + set cmd [linsert $cmd 0 "::vfs::kitdll::vfsop_${subcmd}" $hashkey] + + return [eval $cmd] +} + +proc ::vfs::kitdll::chanhandler {hashkey subcmd args} { + set cmd $args + set cmd [linsert $cmd 0 "::vfs::kitdll::chanop_${subcmd}" $hashkey] + + return [eval $cmd] +} + +### Actual handlers +#### Channel operation handlers +proc ::vfs::kitdll::chanop_initialize {hashkey chanId mode} { + return [list initialize finalize watch read seek] +} + +proc ::vfs::kitdll::chanop_finalize {hashkey chanId} { + unset -nocomplain ::vfs::kitdll::chandata([list $hashkey $chanId]) + + return +} + +proc ::vfs::kitdll::chanop_watch {hashkey chanId eventSpec} { + array set chaninfo $::vfs::kitdll::chandata([list $hashkey $chanId]) + + set chaninfo(watching) $eventSpec + + set ::vfs::kitdll::chandata([list $hashkey $chanId]) [array get chaninfo] + + if {[lsearch -exact $chaninfo(watching) "read"] != -1} { + after 0 [list catch "chan postevent $chanId [list {read}]"] + } + + return +} + +proc ::vfs::kitdll::chanop_read {hashkey chanId bytes} { + array set chaninfo $::vfs::kitdll::chandata([list $hashkey $chanId]) + + set pos $chaninfo(pos) + set len $chaninfo(len) + + if {[lsearch -exact $chaninfo(watching) "read"] != -1} { + after 0 [list catch "chan postevent $chanId [list {read}]"] + } + + if {$pos == $len} { + return "" + } + + set end [expr {$pos + $bytes}] + if {$end > $len} { + set end $len + } + + set data [::vfs::kitdll::data::getData $hashkey $chaninfo(file) $pos $end] + + set dataLen [string length $data] + incr pos $dataLen + + set chaninfo(pos) $pos + + set ::vfs::kitdll::chandata([list $hashkey $chanId]) [array get chaninfo] + + return $data +} + +proc ::vfs::kitdll::chanop_seek {hashkey chanId offset origin} { + array set chaninfo $::vfs::kitdll::chandata([list $hashkey $chanId]) + + set pos $chaninfo(pos) + set len $chaninfo(len) + + switch -- $origin { + "start" - "0" { + set pos $offset + } + "current" - "1" { + set pos [expr {$pos + $offset}] + } + "end" - "2" { + set pos [expr {$len + $offset}] + } + } + + if {$pos < 0} { + set pos 0 + } + + if {$pos > $len} { + set pos $len + } + + set chaninfo(pos) $pos + set ::vfs::kitdll::chandata([list $hashkey $chanId]) [array get chaninfo] + + return $pos +} + +#### VFS operation handlers +proc ::vfs::kitdll::vfsop_stat {hashkey root relative actualpath} { + catch { + set ret [::vfs::kitdll::data::getMetadata $hashkey $relative] + } + + if {![info exists ret]} { + vfs::filesystem posixerror $::vfs::posix(ENOENT) + } + + return $ret +} + +proc ::vfs::kitdll::vfsop_access {hashkey root relative actualpath mode} { + set ret [::vfs::kitdll::data::getMetadata $hashkey $relative] + + if {$mode & 0x2} { + vfs::filesystem posixerror $::vfs::posix(EROFS) + } + + return 1 +} + +proc ::vfs::kitdll::vfsop_matchindirectory {hashkey root relative actualpath pattern types} { + set ret [list] + + catch { + array set metadata [::vfs::kitdll::data::getMetadata $hashkey $relative] + } + + if {![info exists metadata]} { + return [list] + } + + if {$pattern == ""} { + + set children [list $relative] + } else { + set children [::vfs::kitdll::data::getChildren $hashkey $relative] + } + + foreach child $children { + if {![string match $pattern $child]} { + continue + } + + unset -nocomplain metadata + catch { + array set metadata [::vfs::kitdll::data::getMetadata $hashkey $child] + } + + if {[string index $root end] == "/"} { + set child "${root}${child}" + } else { + set child "${root}/${child}" + } + if {[string index $child end] == "/"} { + set child [string range $child 0 end-1] + } + + if {![info exists metadata(type)]} { + continue + } + + set filetype 0 + switch -- $metadata(type) { + "directory" { + set filetype [expr {$filetype | 0x04}] + } + "file" { + set filetype [expr {$filetype | 0x10}] + } + "link" { + set filetype [expr {$filetype | 0x20}] + } + default { + continue + } + } + + if {($filetype & $types) != $types} { + continue + } + + lappend ret $child + } + + return $ret +} + +proc ::vfs::kitdll::vfsop_fileattributes {hashkey root relative actualpath {index -1} {value ""}} { + set attrs [list -owner -group -permissions] + + if {$value != ""} { + vfs::filesystem posixerror $::vfs::posix(EROFS) + } + + if {$index == -1} { + return $attrs + } + + array set metadata [::vfs::kitdll::data::getMetadata $hashkey $relative] + + set attr [lindex $attrs $index] + + switch -- $attr { + "-owner" { + return $metadata(uid) + } + "-group" { + return $metadata(gid) + } + "-permissions" { + if {$metadata(type) == "directory"} { + set metadata(mode) [expr {$metadata(mode) | 040000}] + } + + return [format {0%o} $metadata(mode)] + } + } + + return -code error "Invalid index" +} + +proc ::vfs::kitdll::vfsop_open {hashkey root relative actualpath mode permissions} { + if {$mode != "" && $mode != "r"} { + vfs::filesystem posixerror $::vfs::posix(EROFS) + } + + catch { + array set metadata [::vfs::kitdll::data::getMetadata $hashkey $relative] + } + + if {![info exists metadata]} { + vfs::filesystem posixerror $::vfs::posix(ENOENT) + } + + if {$metadata(type) == "directory"} { + vfs::filesystem posixerror $::vfs::posix(EISDIR) + } + + if {[info command chan] != ""} { + set chan [chan create [list "read"] [list ::vfs::kitdll::chanhandler $hashkey]] + + set ::vfs::kitdll::chandata([list $hashkey $chan]) [list file $relative pos 0 len $metadata(size) watching ""] + + return [list $chan] + } + + if {[info command rechan] == ""} { + catch { + package require rechan + } + } + + if {[info command rechan] != ""} { + set chan [rechan [list ::vfs::kitdll::chanhandler $hashkey] 2] + + set ::vfs::kitdll::chandata([list $hashkey $chan]) [list file $relative pos 0 len $metadata(size) watching ""] + + return [list $chan] + } + + return -code error "No way to generate a channel, need either Tcl 8.5+, \"rechan\"" +} + +##### No-Ops since we are a readonly filesystem +proc ::vfs::kitdll::vfsop_createdirectory {args} { + vfs::filesystem posixerror $::vfs::posix(EROFS) +} +proc ::vfs::kitdll::vfsop_deletefile {args} { + vfs::filesystem posixerror $::vfs::posix(EROFS) +} +proc ::vfs::kitdll::vfsop_removedirectory {args} { + vfs::filesystem posixerror $::vfs::posix(EROFS) +} +proc ::vfs::kitdll::vfsop_utime {} { + vfs::filesystem posixerror $::vfs::posix(EROFS) +} + +package provide vfs::kitdll 1.0 + +::vfs::kitdll::Mount vfs_kitdll_data /tmp ADDED kitdll/buildsrc/kitdll-0.0/vfs_kitdll_data.c Index: kitdll/buildsrc/kitdll-0.0/vfs_kitdll_data.c ================================================================== --- kitdll/buildsrc/kitdll-0.0/vfs_kitdll_data.c +++ kitdll/buildsrc/kitdll-0.0/vfs_kitdll_data.c @@ -0,0 +1,226 @@ +#define HAVE_STDC 1 + +#include +#include +#include + +#include "vfs_kitdll_data.h" + +typedef struct kitdll_data *(cmd_getData_t)(const char *, unsigned long); +typedef unsigned long (cmd_getChildren_t)(const char *, unsigned long *, unsigned long); + +static cmd_getData_t *getCmdData(const char *hashkey) { + /* XXX: TODO: Look up symbol using dlsym() */ + if (strcmp(hashkey, "vfs_kitdll_data") == 0) { + return(kitdll_vfs_kitdll_data_getData); + } + + return(NULL); +} + +static cmd_getChildren_t *getCmdChildren(const char *hashkey) { + /* XXX: TODO: Look up symbol using dlsym() */ + if (strcmp(hashkey, "vfs_kitdll_data") == 0) { + return(kitdll_vfs_kitdll_data_getChildren); + } + + return(NULL); +} + +static int getMetadata(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + cmd_getData_t *cmd_getData; + cmd_getChildren_t *cmd_getChildren; + struct kitdll_data *finfo = NULL; + Tcl_Obj *ret_list, *ret_list_items[20]; + unsigned long num_children; + const char *hashkey; + const char *file; + + if (objc != 3) { + Tcl_SetResult(interp, "wrong # args: should be \"getMetadata hashKey fileName\"", NULL); + + return(TCL_ERROR); + } + + hashkey = Tcl_GetString(objv[1]); + file = Tcl_GetString(objv[2]); + + cmd_getData = getCmdData(hashkey); + cmd_getChildren = getCmdChildren(hashkey); + + if (cmd_getData == NULL || cmd_getChildren == NULL) { + Tcl_SetResult(interp, "No such hashkey", NULL); + + return(TCL_ERROR); + } + + finfo = cmd_getData(file, 0); + + if (finfo == NULL) { + Tcl_SetResult(interp, "No such file or directory", NULL); + + return(TCL_ERROR); + } + + /* Values that can be derived from "finfo" */ + ret_list_items[0] = Tcl_NewStringObj("type", 4); + ret_list_items[2] = Tcl_NewStringObj("mode", 4); + ret_list_items[4] = Tcl_NewStringObj("nlink", 5); + + if (finfo->type == KITDLL_FILETYPE_DIR) { + num_children = cmd_getChildren(file, NULL, 0); + + ret_list_items[1] = Tcl_NewStringObj("directory", 9); + ret_list_items[3] = Tcl_NewLongObj(040555); + ret_list_items[5] = Tcl_NewLongObj(num_children); + } else { + ret_list_items[1] = Tcl_NewStringObj("file", 4); + ret_list_items[3] = Tcl_NewLongObj(0444); + ret_list_items[5] = Tcl_NewLongObj(1); + } + + ret_list_items[6] = Tcl_NewStringObj("ino", 3); + ret_list_items[7] = Tcl_NewLongObj(finfo->index); + + ret_list_items[8] = Tcl_NewStringObj("size", 4); + ret_list_items[9] = Tcl_NewLongObj(finfo->size); + + /* Dummy values */ + ret_list_items[10] = Tcl_NewStringObj("uid", 3); + ret_list_items[11] = Tcl_NewStringObj("0", 1); + + ret_list_items[12] = Tcl_NewStringObj("gid", 3); + ret_list_items[13] = Tcl_NewStringObj("0", 1); + + ret_list_items[14] = Tcl_NewStringObj("atime", 5); + ret_list_items[15] = Tcl_NewStringObj("0", 1); + + ret_list_items[16] = Tcl_NewStringObj("mtime", 5); + ret_list_items[17] = Tcl_NewStringObj("0", 1); + + ret_list_items[18] = Tcl_NewStringObj("ctime", 5); + ret_list_items[19] = Tcl_NewStringObj("0", 1); + + ret_list = Tcl_NewListObj(sizeof(ret_list_items) / sizeof(ret_list_items[0]), ret_list_items); + + Tcl_SetObjResult(interp, ret_list); + + return(TCL_OK); +} + +static int getData(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + if (objc < 3 || objc > 5) { + Tcl_SetResult(interp, "wrong # args: should be \"getData hashKey fileName ?start? ?end?\"", NULL); + + return(TCL_ERROR); + } + return(TCL_ERROR); +} + +static int getChildren(ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { + cmd_getChildren_t *cmd_getChildren; + cmd_getData_t *cmd_getData; + struct kitdll_data *finfo = NULL; + unsigned long num_children, idx; + unsigned long *children; + const char *hashkey; + const char *file; + const char *child; + Tcl_Obj *ret_list, *ret_curr_obj; + + if (objc != 3) { + Tcl_SetResult(interp, "wrong # args: should be \"getChildren hashKey fileName\"", NULL); + + return(TCL_ERROR); + } + + hashkey = Tcl_GetString(objv[1]); + file = Tcl_GetString(objv[2]); + + cmd_getData = getCmdData(hashkey); + cmd_getChildren = getCmdChildren(hashkey); + + if (cmd_getData == NULL || cmd_getChildren == NULL) { + Tcl_SetResult(interp, "No such hashkey", NULL); + + return(TCL_ERROR); + } + + finfo = cmd_getData(file, 0); + + if (finfo == NULL) { + Tcl_SetResult(interp, "No such file or directory", NULL); + + return(TCL_ERROR); + } + + if (finfo->type != KITDLL_FILETYPE_DIR) { + Tcl_SetResult(interp, "Not a directory", NULL); + + return(TCL_ERROR); + } + + num_children = cmd_getChildren(file, NULL, 0); + + if (num_children == 0) { + /* Return immediately if there are no children */ + Tcl_SetResult(interp, "", NULL); + + return(TCL_OK); + } + + ret_list = Tcl_NewObj(); + if (ret_list == NULL) { + Tcl_SetResult(interp, "Failed to allocate new object", NULL); + + return(TCL_ERROR); + } + + children = malloc(sizeof(*children) * num_children); + + num_children = cmd_getChildren(file, children, num_children); + + for (idx = 0; idx < num_children; idx++) { + finfo = cmd_getData(NULL, children[idx]); + + if (finfo == NULL || finfo->name == NULL) { + continue; + } + + child = finfo->name; + + ret_curr_obj = Tcl_NewStringObj(child, strlen(child)); + + Tcl_ListObjAppendList(interp, ret_list, ret_curr_obj); + } + + free(children); + + Tcl_SetObjResult(interp, ret_list); + + return(TCL_OK); +} + +int Vfs_kitdll_data_Init(Tcl_Interp *interp) { + Tcl_Command tclCreatComm_ret; + int tclPkgProv_ret; + + tclCreatComm_ret = Tcl_CreateObjCommand(interp, "::vfs::kitdll::data::getMetadata", getMetadata, NULL, NULL); + if (!tclCreatComm_ret) { + return(TCL_ERROR); + } + + tclCreatComm_ret = Tcl_CreateObjCommand(interp, "::vfs::kitdll::data::getData", getData, NULL, NULL); + if (!tclCreatComm_ret) { + return(TCL_ERROR); + } + + tclCreatComm_ret = Tcl_CreateObjCommand(interp, "::vfs::kitdll::data::getChildren", getChildren, NULL, NULL); + if (!tclCreatComm_ret) { + return(TCL_ERROR); + } + + tclPkgProv_ret = Tcl_PkgProvide(interp, "vfs::kitdll::data", "1.0"); + + return(tclPkgProv_ret); +}