Browse Source

Migrated from NagaeVFS v1.1.0.

ZRY 1 month ago
parent
commit
7c921a5ee4

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+dist/
+target/
+debug/
+Cargo.lock
+*.pdb
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+*.wasm
+*.test
+*.out
+go.work.sum

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 7 - 0
.idea/misc.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="XMakeProjectSettings">
+    <option name="currentArchitecture" value="x86" />
+    <option name="workingDirectory" value="$PROJECT_DIR$" />
+  </component>
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/openNGVFS.iml" filepath="$PROJECT_DIR$/.idea/openNGVFS.iml" />
+    </modules>
+  </component>
+</project>

+ 14 - 0
.idea/openNGVFS.iml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/init-wasm/openngvfs_init_wasm_lib/src" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/testing/standalone-test-0001/openngvfs_init_wasm_test/src" isTestSource="false" />
+      <excludeFolder url="file://$MODULE_DIR$/init-wasm/openngvfs_init_wasm_lib/target" />
+      <excludeFolder url="file://$MODULE_DIR$/testing/standalone-test-0001/openngvfs_init_wasm_test/target" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>

+ 8 - 0
LICENSE

@@ -0,0 +1,8 @@
+MIT License
+Copyright (c) <year> <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 8 - 1
README.md

@@ -1,3 +1,10 @@
 # openNGVFS
 
-The open source version of NagaeVFS.
+The open source version of NagaeVFS.
+
+NagaeVFS的开源版本。
+
+Documentation in `docs` directory.
+Currently only Chinese documents are provided.
+
+文档在 `docs` 目录。当前只有中文文档。

+ 11 - 0
docs/init_wasi_export.yaml

@@ -0,0 +1,11 @@
+auto_mount:
+  desc: "执行自动挂载,即WASM的实际功能入口"
+  results:
+    - name: exit_code
+      type: i32
+      desc: "挂载结果"
+      enum:
+        "0": "挂载成功"
+        "!=0": "挂载失败"
+_start:
+  desc: "WASI的主入口"

+ 311 - 0
docs/init_wasi_import.yaml

@@ -0,0 +1,311 @@
+ngvfs_init:
+  api_ver_get:
+    desc: "获得NGVFS API版本"
+    params:
+      - name: ptrMajorVersion
+        type: i32
+        xtyp: "*mut i32"
+        desc: "存放获取到的MajorVersion的i32变量指针"
+      - name: ptrMinorVersion
+        type: i32
+        xtyp: "*mut i32"
+        desc: "存放获取到的MinorVersion的i32变量指针"
+      - name: ptrPatchVersion
+        type: i32
+        xtyp: "*mut i32"
+        desc: "存放获取到的PatchVersion的i32变量指针"
+  exkv_get_len:
+    desc: "通过key获得exkv中value的长度"
+    params:
+      - name: ptrKey
+        type: i32
+        xtyp: "*const u8"
+        desc: "key的指针"
+      - name: lenKey
+        type: i32
+        xtyp: "usize"
+        desc: "key的长度"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-2": "key不存在"
+          "-1": "读取key时内存越界"
+          ">=0": "返回值即为value的长度"
+  exkv_get_val:
+    desc: "通过key获得exkv中的value"
+    params:
+      - name: ptrKey
+        type: i32
+        xtyp: "*const u8"
+        desc: "key的指针"
+      - name: lenKey
+        type: i32
+        xtyp: "usize"
+        desc: "key的长度"
+      - name: ptrBuf
+        type: i32
+        xtyp: "*mut u8"
+        desc: "存放值的缓冲区指针"
+      - name: lenBuf
+        type: i32
+        xtyp: "usize"
+        desc: "缓冲区长度。缓冲区不足则值会被截断"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-3": "写入buf时内存越界"
+          "-2": "key不存在"
+          "-1": "读取key时内存越界"
+          "0": "缓冲区长度小于等于0"
+          ">0": "写入缓冲区的值的长度"
+  vfs_mount:
+    desc: "挂载指定句柄的afs文件系统实例至指定挂载点"
+    params:
+      - name: hAfs
+        type: i32
+        desc: "afs实例句柄"
+      - name: ptrMntPath
+        type: i32
+        xtyp: "*const u8"
+        desc: "挂载点路径的指针"
+      - name: lenMntPath
+        type: i32
+        xtyp: "usize"
+        desc: "挂载点路径的长度"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-4": "挂载时发生内部错误,详见日志"
+          "-3": "挂载点已经被挂载"
+          "-2": "读取挂载点路径时内存越界"
+          "-1": "afs句柄无效"
+          "0": "挂载成功"
+  vfs_mkdir:
+    desc: "创建目录,类似mkdir -p,用于准备挂载点"
+    params:
+      - name: ptrPath
+        type: i32
+        xtyp: "*const u8"
+        desc: "目录路径的指针"
+      - name: lenPath
+        type: i32
+        xtyp: "usize"
+        desc: "目录路径的长度"
+      - name: mode
+        type: i32
+        desc: "目录的权限模式"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-2": "创建目录失败,详见日志"
+          "-1": "读取路径时内存越界"
+          "0": "创建成功"
+  afs_free:
+    desc: "释放指定句柄的afs实例"
+    params:
+      - name: hAfs
+        type: i32
+        desc: "afs实例句柄"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-1": "句柄不存在"
+          "0": "释放成功"
+  afs_mkdir:
+    desc: "在afero.Fs实例上创建目录,类似mkdir -p,用于准备挂载点,但是是在afero.Fs实例上进行。"
+    params:
+      - name: hAfs
+        type: i32
+        desc: "afs实例句柄"
+      - name: ptrPath
+        type: i32
+        xtyp: "*const u8"
+        desc: "Path的指针"
+      - name: lenPath
+        type: i32
+        xtyp: "usize"
+        desc: "Path的长度"
+      - name: mode
+        type: i32
+        desc: "目录的权限模式"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-3": "读取Path时内存越界"
+          "-2": "afs实例句柄无效"
+          "-1": "创建目录失败,详见日志"
+          "0": "创建目录成功"
+  afs_create_osfs:
+    desc: "创建afero.OsFs实例"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-1": "申请afs句柄资源失败"
+          ">0": "创建的afs实例的句柄"
+  afs_create_memfs:
+    desc: "创建afero.MemMapFs实例"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-1": "申请afs句柄资源失败"
+          ">0": "创建的afs实例的句柄"
+  afs_create_bpfs:
+    desc: "创建afero.BasePathFs实例"
+    params:
+      - name: hBaseAfs
+        type: i32
+        desc: "基于的afs实例句柄"
+      - name: ptrBasePath
+        type: i32
+        xtyp: "*const u8"
+        desc: "BasePath的指针"
+      - name: lenBasePath
+        type: i32
+        xtyp: "usize"
+        desc: "BasePath的长度"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-3": "读取BasePath时内存越界"
+          "-2": "afs实例句柄无效"
+          "-1": "申请afs句柄资源失败"
+          ">0": "创建的afs实例的句柄"
+  afs_create_regfs:
+    desc: "创建afero.RegexpFs实例,使用字符串形式的golang正则表达式"
+    params:
+      - name: hBaseAfs
+        type: i32
+        desc: "基于的afs实例句柄"
+      - name: ptrRegExp
+        type: i32
+        xtyp: "*const u8"
+        desc: "RegExp的指针"
+      - name: lenRegExp
+        type: i32
+        xtyp: "usize"
+        desc: "RegExp的长度"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-4": "编译正则表达式失败"
+          "-3": "读取RegExp时内存越界"
+          "-2": "afs实例句柄无效"
+          "-1": "申请afs句柄资源失败"
+          ">0": "创建的afs实例的句柄"
+  afs_create_rofs:
+    desc: "创建afero.ReadOnlyFs实例"
+    params:
+      - name: hBaseAfs
+        type: i32
+        desc: "基于的afs实例句柄"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-2": "afs实例句柄无效"
+          "-1": "申请afs句柄资源失败"
+          ">0": "创建的afs实例的句柄"
+  afs_create_cowfs:
+    desc: "创建afero.CopyOnWriteFs实例"
+    params:
+      - name: hRoAfs
+        type: i32
+        desc: "基于的只读afs实例句柄"
+      - name: hWrAfs
+        type: i32
+        desc: "基于的可写afs实例句柄"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-3": "可写afs实例句柄无效"
+          "-2": "只读afs实例句柄无效"
+          "-1": "申请afs句柄资源失败"
+          ">0": "创建的afs实例的句柄"
+  afs_create_corfs:
+    desc: "创建afero.CacheOnReadFs实例"
+    params:
+      - name: hRoAfs
+        type: i32
+        desc: "基于的afs实例句柄"
+      - name: hWrAfs
+        type: i32
+        desc: "缓存afs实例句柄"
+      - name: cacheTime
+        type: i32
+        desc: "缓存时间,单位为秒,0为永久缓存"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-4": "缓存时间小于0"
+          "-3": "可写afs实例句柄无效"
+          "-2": "只读afs实例句柄无效"
+          "-1": "申请afs句柄资源失败"
+          ">0": "创建的afs实例的句柄"
+  afs_create_bep_ovfs:
+    desc: "创建gtihub.com/bep/overlayfs.OverlayFs实例"
+    params:
+      - name: ptrAfsArray
+        type: i32
+        xtyp: "*const i32"
+        desc: "文件系统层句柄数组的指针,数组从左往右,越左边的优先级越高"
+      - name: lenAfsArray
+        type: i32
+        xtyp: "usize"
+        desc: "文件系统层句柄数组的长度"
+      - name: bWritable
+        type: i32
+        xtyp: "enum"
+        desc: "是否允许写入"
+        enum:
+          "<=0": "不允许"
+          ">0": "允许"
+    results:
+      - name: result
+        type: i32
+        xtyp: "enum"
+        desc: "结果"
+        enum:
+          "-5": "申请afs句柄资源失败"
+          "-4": "读取文件系统层数组时遇到无效的句柄"
+          "-3": "读取文件系统层数组时元素长度不正确"
+          "-2": "读取文件系统层数组时内存越界"
+          "-1": "文件系统层数为0"
+          ">0": "创建的afs实例的句柄"

BIN
docs/ngvfs_docs.pdf


+ 349 - 0
docs/ngvfs_docs.typ

@@ -0,0 +1,349 @@
+#import "../docs_common/template/template.typ" : ZRYGenericDocs
+#import "@preview/tablex:0.0.8": tablex, colspanx, rowspanx
+#import "@preview/oxifmt:0.2.0": strfmt
+
+#let pageCounting = locate(loc=>counter(page).final(loc).at(0))
+#let title = "openNGVFS 文档"
+#let version = (
+	major: 1,
+	minor: 1,
+	patch: 0,
+)
+#let semVerStr = strfmt("{}.{}.{}", version.major, version.minor, version.patch)
+
+#show: body => ZRYGenericDocs(
+    title: title,
+    subTitle: "",
+    titleDesc: "",
+    docVer: "API Ver. " + semVerStr,
+    headerSettings: (
+        enable: true,
+        ascent: 20%,
+        content: locate(loc => {
+          let elems = query(selector(heading).before(loc), loc)
+          if counter(page).at(loc).first() > 1 [
+            #text(
+              weight: "bold",
+              size: 0.9em,
+            )[
+              #title
+            ]
+            #h(1fr)
+            #text(
+              weight: "thin",
+              size: 0.7em,
+            )[
+              #counter(page).display() / #pageCounting
+            ]
+            #line(length: 100%)
+          ]
+        }),
+    ),
+    footerSettings: (
+        enable: true,
+        descent: 30%,
+        content: locate(loc => {
+          if counter(page).at(loc).first() > 1 [
+            #line(length: 100%)
+            #text(
+              weight: "thin",
+              size: 0.6em,
+            )[
+              公开文档
+            ]
+          ]
+        }),
+    ),
+    body
+)
+
+= 项目说明
+
+该项目基于商业项目ProjectNagae下属的NagaeVFS V1.1.0,为NagaeVFS的开源版本。该版本使用MIT License开源。注意,NagaeVFS后续版本将不再与openNGVFS兼容。
+
+= 版本说明
+
+当前版本从NagaeVFS V1.1.0衍生,经过一定程度的测试,但使用前请自行进行必要的功能测试。
+
+当前版本提供以下功能。
+
+== VFS
+
+一个可挂载的虚拟文件系统。其主机接口兼容afero.Fs。
+
+== INIT WASM
+
+用于初始化文件系统挂载的WASM虚拟机。
+
+该虚拟机提供以下接口:
++ 在主机上创建和释放afero.Fs对象的接口。
++ 挂载主机上被创建的afero.Fs对象的接口。
++ 在主机VFS上创建目录的接口。
++ 读取配置文件中ExKV键值对内容的接口。
+
+=== 可创建的afs对象
+
+==== 来自afero.Fs的类型
+
+当前版本可创建的afero.Fs对象,由afero包本身提供的有:
+
+- afero.OsFs
+- afero.MemMapFs
+- afero.BasePathFs
+- afero.RegexpFs
+- afero.ReadOnlyFs
+- afero.CopyOnWriteFs
+- afero.CacheOnReadFs
+
+其中经过简单测试并通过的有:
+
+- afero.OsFs
+- afero.MemMapFs
+- afero.BasePathFs
+- afero.ReadOnlyFs
+
+其中经过测试有问题的:
+
+- afero.RegexpFs
+   - Readdir功能在遇到无法匹配的文件时产生error
+     
+     该问题来自afero.RegexpFs本身,故不提供其修正。
+
+     谨慎考虑使用RegexpFs。
+
+==== 来自第三方的
+
+- `"github.com/bep/overlayfs" overlayfs.OverlayFs`
+    
+    经过测试,但有如下问题:
+      - 不支持在子目录下创建文件,会产生错误
+        
+        `The system cannot find the path specified.`
+
+= INIT WASI接口
+
+== WASM导出函数
+#{
+  let data = yaml("init_wasi_export.yaml")
+  for funcPair in data {
+    {
+      let funcName = funcPair.at(0)
+      [==== 函数 #raw(funcName)]
+      let funcPart = funcPair.at(1)
+      let funcParams = funcPart.at("params", default: none)
+      let funcResults = funcPart.at("results", default: none)
+      let prototypeParams = if funcParams != none and funcParams.len() > 0 {
+        let prototypeParamsList = ()
+        for iParam in funcParams {
+          prototypeParamsList.push(iParam.name + ": " + iParam.type)
+        }
+        prototypeParamsList.join(",")
+      }else{""}
+      let prototypeCode = "fn " + funcName + "(" + prototypeParams + ")"
+      let prototypeResult = if funcResults != none and funcResults.len() > 0 {
+        let prototypeResultList = ()
+        for iResult in funcResults {
+          prototypeResultList.push(iResult.name + ": " + iResult.type)
+        }
+        " -> (" + prototypeResultList.join(",") + ")"
+      }else{""}
+      prototypeCode = prototypeCode + prototypeResult + ";"
+      let prototype = raw(prototypeCode, lang:"rust")
+      let tableContent = (
+        [*名称*], colspanx(4)[#funcPair.at(0)],
+        [*说明*], colspanx(4)[#funcPart.desc],
+        [*原型*], colspanx(4)[#prototype],
+      )
+      if funcParams != none and funcParams.len() > 0 {
+        let lenParams = funcParams.len()
+        tableContent.push(rowspanx(lenParams+1)[*参数*])
+        tableContent.push([*名称*])
+        tableContent.push([*WASM类型*])
+        tableContent.push([*语义类型*])
+        tableContent.push([*说明*])
+        for param in funcParams {
+          let paramEnum = param.at("enum", default: none)
+          tableContent.push(param.name)
+          tableContent.push(param.type)
+          tableContent.push(if param.xtyp != none {param.xtyp} else {param.type})
+          tableContent.push(
+            if paramEnum != none [
+              #param.desc
+              #let paramEnumTable = ()
+              #for peti in paramEnum {
+                paramEnumTable.push(peti.at(0))
+                paramEnumTable.push(peti.at(1))
+              }
+              #tablex(
+                columns: 2,
+                [*值*], [*说明*],
+                ..paramEnumTable
+              )
+            ]else[#param.desc]
+          )
+        }
+      }
+      if funcResults != none and funcResults.len() > 0 {
+        let lenResults = funcResults.len()
+        tableContent.push(rowspanx(lenResults+1)[*返回值*])
+        tableContent.push([*名称*])
+        tableContent.push([*WASM类型*])
+        tableContent.push([*语义类型*])
+        tableContent.push([*说明*])
+        for param in funcResults {
+          let paramEnum = param.at("enum", default: none)
+          let paramXtyp = param.at("xtyp", default: none)
+          tableContent.push(param.name)
+          tableContent.push(param.type)
+          tableContent.push(if paramXtyp != none {paramXtyp} else {param.type})
+          tableContent.push(
+            if paramEnum != none [
+              #param.desc
+              #let paramEnumTable = ()
+              #for peti in paramEnum {
+                paramEnumTable.push(peti.at(0))
+                paramEnumTable.push(peti.at(1))
+              }
+              #tablex(
+                columns: 2,
+                [*值*], [*说明*],
+                ..paramEnumTable
+              )
+            ]else[#param.desc]
+          )
+        }
+      }
+      tablex(
+        columns: 5,
+        ..tableContent
+      )
+    }
+  }
+} 
+
+== 主机导出模块
+
+#{
+  let data = yaml("init_wasi_import.yaml")
+  for modPair in data {
+    [=== 模块 #raw(modPair.at(0))]
+    for funcPair in modPair.at(1) {
+      {
+        let funcName = funcPair.at(0)
+        [==== 函数 #raw(funcName)]
+        let funcPart = funcPair.at(1)
+        let funcParams = funcPart.at("params", default: none)
+        let funcResults = funcPart.at("results", default: none)
+        let prototypeParams = if funcParams != none and funcParams.len() > 0 {
+          let prototypeParamsList = ()
+          for iParam in funcParams {
+            prototypeParamsList.push(iParam.name + ": " + iParam.type)
+          }
+          prototypeParamsList.join(",")
+        }else{""}
+        let prototypeCode = "fn " + funcName + "(" + prototypeParams + ")"
+        let prototypeResult = if funcResults != none and funcResults.len() > 0 {
+          let prototypeResultList = ()
+          for iResult in funcResults {
+            prototypeResultList.push(iResult.name + ": " + iResult.type)
+          }
+          " -> (" + prototypeResultList.join(",") + ")"
+        }else{""}
+        prototypeCode = prototypeCode + prototypeResult + ";"
+        let prototype = raw(prototypeCode, lang:"rust")
+        let tableContent = (
+          [*名称*], colspanx(4)[#funcPair.at(0)],
+          [*说明*], colspanx(4)[#funcPart.desc],
+          [*原型*], colspanx(4)[#prototype],
+        )
+        if funcParams != none and funcParams.len() > 0 {
+          let lenParams = funcParams.len()
+          tableContent.push(rowspanx(lenParams+1)[*参数*])
+          tableContent.push([*名称*])
+          tableContent.push([*WASM类型*])
+          tableContent.push([*语义类型*])
+          tableContent.push([*说明*])
+          for param in funcParams {
+            let paramEnum = param.at("enum", default: none)
+            let paramXtyp = param.at("xtyp", default: none)
+            tableContent.push(param.name)
+            tableContent.push(param.type)
+            tableContent.push(if paramXtyp != none {paramXtyp} else {param.type})
+            tableContent.push(
+              if paramEnum != none [
+                #param.desc
+                #let paramEnumTable = ()
+                #for peti in paramEnum {
+                  paramEnumTable.push(peti.at(0))
+                  paramEnumTable.push(peti.at(1))
+                }
+                #tablex(
+                  columns: 2,
+                  [*值*], [*说明*],
+                  ..paramEnumTable
+                )
+              ]else[#param.desc]
+            )
+          }
+        }
+        if funcResults != none and funcResults.len() > 0 {
+          let lenResults = funcResults.len()
+          tableContent.push(rowspanx(lenResults+1)[*返回值*])
+          tableContent.push([*名称*])
+          tableContent.push([*WASM类型*])
+          tableContent.push([*语义类型*])
+          tableContent.push([*说明*])
+          for param in funcResults {
+            let paramEnum = param.at("enum", default: none)
+            tableContent.push(param.name)
+            tableContent.push(param.type)
+            tableContent.push(if param.xtyp != none {param.xtyp} else {param.type})
+            tableContent.push(
+              if paramEnum != none [
+                #param.desc
+                #let paramEnumTable = ()
+                #for peti in paramEnum {
+                  paramEnumTable.push(peti.at(0))
+                  paramEnumTable.push(peti.at(1))
+                }
+                #tablex(
+                  columns: 2,
+                  [*值*], [*说明*],
+                  ..paramEnumTable
+                )
+              ]else[#param.desc]
+            )
+          }
+        }
+        tablex(
+          columns: 5,
+          ..tableContent
+        )
+      }
+    }
+  }
+} 
+
+== WASM侧Rust Crate
+
+提供有用于WASM侧的接口包装Rust Crate。
+
+目前本文档内暂不提供该Crate的文档。
+
+该包的交互式文档可使用rustdoc生成:
+
+```bash
+cd init-wasm
+just doc_html
+```
+
+生成的文档在`init-wasm/ngvfs_init_wasm_lib/target/doc/ngvfs_init_wasm_lib`
+
+若安装有`SimpleHttpTestServer`,可继续使用如下命令:
+
+```bash
+# 端口号可换成其他值
+just serv_doc "localhost:8088"
+```
+
+然后使用浏览器打开 #link("http://localhsot:8088/")。

+ 366 - 0
docs_common/template/template.typ

@@ -0,0 +1,366 @@
+#let datetimeNow = datetime.today();
+
+#let fliPar(body)={
+    par(
+        [#h(2em)#body]
+    )
+}
+
+#let ZRYGenericDocs(
+    authors: ("ZRY",),
+    title: "标题",
+    subTitle: "副标题",
+    titleDesc: "标题说明",
+    date: datetimeNow.display("[year]-[month]-[day]"),
+    docVer: "V0.1",
+    pageSettings: (
+        paper: "a4",
+        leftMargin: 1.25in,
+        topMargin: 1in,
+        rightMargin: 1.25in,
+        bottomMargin: 1in,
+        foreground: none,
+        background: none,
+        columns: 1,
+        flipped: false,
+    ),
+    fontSets: (
+        sans: "Noto Sans CJK SC",
+        //sans: "Source Han Sans",
+        serif: "Noto Serif CJK SC",
+        //serif: "Source Han Serif VF"
+    ),
+    headerSettings: (
+        enable: false,
+        ascent: 30%,
+        content: none,
+    ),
+    footerSettings: (
+        enable: false,
+        descent: 30%,
+        content: none,
+    ),
+    pageNumberingSettings:(
+        content: none,
+        align: center + bottom,
+    ),
+    coverStyle:(
+        titleSize: 36pt,
+        titleWeight: 600,
+        subTitleSize: 24pt,
+        subTitleWeight: 600,
+        titleDescSize: 16pt,
+        titleDescWeight: 400,
+        dateSize: 16pt,
+        dateWeight: 400,
+        authorsSize: 16pt,
+        authorsWeight: 600,
+        docVerSize: 16pt,
+        docVerWeight: 400,
+        topMargin: 72pt,
+        subTitleMargin: 72pt,
+        dateMargin: 16pt,
+        authorsMargin: 16pt,
+        titleDescMargin: 72pt,
+        authorsJoinSymbol: ","
+    ),
+    textStyle:(
+        h1:(
+            sans: false,
+            size: 22pt,
+            weight: 800,
+            style: "italic",
+            stretch: 100%,
+            fill: rgb("#000000"),
+            tracking: 0pt,
+            spacing: 100%,
+            overhang: true,
+            vspaceBottom: 12pt,
+        ),
+        h2:(
+            sans: true,
+            size: 16pt,
+            weight: 400,
+            style: "normal",
+            stretch: 100%,
+            fill: rgb("#000000"),
+            tracking: 0pt,
+            spacing: 100%,
+            overhang: true,
+            vspaceBottom: 6pt,
+        ),
+        h3: (
+            sans: false,
+            size: 14pt,
+            weight: 900,
+            style: "normal",
+            stretch: 100%,
+            fill: rgb("#000000"),
+            tracking: 0pt,
+            spacing: 100%,
+            overhang: true,
+            vspaceBottom: 12pt,
+        ),
+        h4: (
+            sans: true,
+            size: 14pt,
+            weight: 500,
+            style: "normal",
+            stretch: 100%,
+            fill: rgb("#000000"),
+            tracking: 0pt,
+            spacing: 100%,
+            overhang: true,
+            vspaceBottom: 12pt,
+        ),
+        h5: (
+            sans: true,
+            size: 14pt,
+            weight: 300,
+            style: "italic",
+            stretch: 100%,
+            fill: rgb("#000000"),
+            tracking: 0pt,
+            spacing: 100%,
+            overhang: true,
+            vspaceBottom: 8pt,
+        ),
+        text: (
+            size: 12pt,
+            weight: 200,
+        ),
+    ),
+    body
+) = {
+    let hdr = if headerSettings.enable {
+        (
+            header: headerSettings.content,
+            header-ascent: headerSettings.ascent,
+        )
+    } else {
+        (
+            header: none,
+            header-ascent: 30%,
+        )
+    }
+    let ftr = if footerSettings.enable {
+        (
+            footer: footerSettings.content,
+            footer-descent: footerSettings.descent,
+        )
+    } else {
+        (
+            footer: none,
+            footer-descent: 30%,
+        )
+    }
+    // Page Setting
+    set page(
+        paper: pageSettings.paper,
+        margin: (
+            left: pageSettings.leftMargin,
+            right: pageSettings.rightMargin,
+            top: pageSettings.topMargin,
+            bottom: pageSettings.bottomMargin
+        ),
+        foreground: pageSettings.foreground,
+        background: pageSettings.background,
+        columns: pageSettings.columns,
+        flipped: pageSettings.flipped,
+        numbering: pageNumberingSettings.content,
+        number-align: pageNumberingSettings.align,
+        header: hdr.header,
+        header-ascent: hdr.header-ascent,
+        footer: ftr.footer,
+        footer-descent: ftr.footer-descent, 
+    )
+    // Document Meta Setting
+    set document(
+        title: title,
+        author: authors
+    )
+
+    // ==== Cover Begin ====
+    v(coverStyle.topMargin)
+    // Title
+    align(center, text(
+        font: fontSets.sans,
+        weight: coverStyle.titleWeight,
+        size: coverStyle.titleSize,
+        title
+    ))
+    v(coverStyle.subTitleMargin)
+    // subTitle
+    align(center, text(
+        font: fontSets.sans,
+        weight: coverStyle.subTitleWeight,
+        size: coverStyle.subTitleSize,
+        subTitle
+    ))
+
+    align(bottom, [
+        // titleDesc
+        #align(center, text(
+            font: fontSets.sans,
+            weight: coverStyle.titleDescWeight,
+            size: coverStyle.titleDescSize,
+            titleDesc
+        ))
+        #v(coverStyle.titleDescMargin)
+        // authors
+        #align(center, text(
+            font: fontSets.sans,
+            weight: coverStyle.authorsWeight,
+            size: coverStyle.authorsSize,
+            authors.join(coverStyle.authorsJoinSymbol)
+        ))
+        #v(coverStyle.authorsMargin)
+        // date
+        #align(center, text(
+            font: fontSets.sans,
+            weight: coverStyle.dateWeight,
+            size: coverStyle.dateSize,
+            date
+        ))
+        #v(coverStyle.dateMargin)
+        // docVer
+        #align(center, text(
+            font: fontSets.sans,
+            weight: coverStyle.docVerWeight,
+            size: coverStyle.docVerSize,
+            docVer
+        ))
+    ])
+    // ==== Cover End ====
+    pagebreak()
+
+    // Default Font Setting
+    set text(
+        font: fontSets.serif,
+        weight: textStyle.text.weight,
+        size: textStyle.text.size,
+        lang: "zh",
+    )
+    // Paragraph Setting
+    /*
+    show par: set block(spacing: 0.65em)
+    set par(
+        leading: 0.65em,
+        //first-line-indent: 2em,
+        justify: true,
+    )
+    */
+
+    // ==== Heading Style Setting Begin ====
+    set heading(numbering: "1.1.1.1.1")
+    // H1
+    show heading.where(level: 1): it => block(width: 100%)[
+        #let fontFamily = if textStyle.h1.sans {
+            fontSets.sans
+        } else {
+            fontSets.serif
+        }
+        #text(
+            font: fontFamily,
+            size: textStyle.h1.size,
+            weight: textStyle.h1.weight,
+            style: textStyle.h1.style,
+            stretch: textStyle.h1.stretch,
+            fill: textStyle.h1.fill,
+            tracking: textStyle.h1.tracking,
+            spacing: textStyle.h1.spacing,
+            overhang: textStyle.h1.overhang,
+            counter(heading).display() + h(1em) + it.body
+        )
+        #v(textStyle.h1.vspaceBottom)
+    ]
+    // H2
+    show heading.where(level: 2): it => block(width: 100%)[
+        #let fontFamily = if textStyle.h2.sans {
+            fontSets.sans
+        } else {
+            fontSets.serif
+        }
+        #text(
+            font: fontFamily,
+            size: textStyle.h2.size,
+            weight: textStyle.h2.weight,
+            style: textStyle.h2.style,
+            stretch: textStyle.h2.stretch,
+            fill: textStyle.h2.fill,
+            tracking: textStyle.h2.tracking,
+            spacing: textStyle.h2.spacing,
+            overhang: textStyle.h2.overhang,
+            counter(heading).display() + h(1em) + it.body
+        )
+        #v(textStyle.h2.vspaceBottom)
+    ]
+    // H3
+    show heading.where(level: 3): it => block(width: 100%)[
+        #let fontFamily = if textStyle.h3.sans {
+            fontSets.sans
+        } else {
+            fontSets.serif
+        }
+        #text(
+            font: fontFamily,
+            size: textStyle.h3.size,
+            weight: textStyle.h3.weight,
+            style: textStyle.h3.style,
+            stretch: textStyle.h3.stretch,
+            fill: textStyle.h3.fill,
+            tracking: textStyle.h3.tracking,
+            spacing: textStyle.h3.spacing,
+            overhang: textStyle.h3.overhang,
+            counter(heading).display() + h(1em) + it.body
+        )
+        #v(textStyle.h3.vspaceBottom)
+    ]
+    // H4
+    show heading.where(level: 4): it => block(width: 100%)[
+        #let fontFamily = if textStyle.h4.sans {
+            fontSets.sans
+        } else {
+            fontSets.serif
+        }
+        #text(
+            font: fontFamily,
+            size: textStyle.h4.size,
+            weight: textStyle.h4.weight,
+            style: textStyle.h4.style,
+            stretch: textStyle.h4.stretch,
+            fill: textStyle.h4.fill,
+            tracking: textStyle.h4.tracking,
+            spacing: textStyle.h4.spacing,
+            overhang: textStyle.h4.overhang,
+            counter(heading).display() + h(1em) + it.body
+        )
+        #v(textStyle.h4.vspaceBottom)
+    ]
+    // H5
+    show heading.where(level: 5): it => block(width: 100%)[
+        #let fontFamily = if textStyle.h5.sans {
+            fontSets.sans
+        } else {
+            fontSets.serif
+        }
+        #text(
+            font: fontFamily,
+            size: textStyle.h5.size,
+            weight: textStyle.h5.weight,
+            style: textStyle.h5.style,
+            stretch: textStyle.h5.stretch,
+            fill: textStyle.h5.fill,
+            tracking: textStyle.h5.tracking,
+            spacing: textStyle.h5.spacing,
+            overhang: textStyle.h5.overhang,
+            counter(heading).display() + h(1em) + it.body
+        )
+        #v(textStyle.h5.vspaceBottom)
+    ]
+    // ==== Title Style Setting End ====
+
+    // ==== Body Begin ====
+    body
+    // ==== Body End ====
+}

+ 6 - 0
go.work

@@ -0,0 +1,6 @@
+go 1.21.6
+
+use (
+	openngvfs
+	testing/standalone-test-0001/go
+)

+ 9 - 0
init-wasm/openngvfs_init_wasm_lib/Cargo.toml

@@ -0,0 +1,9 @@
+[package]
+name = "openngvfs_init_wasm_lib"
+version = "1.1.0"
+edition = "2021"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]

+ 23 - 0
init-wasm/openngvfs_init_wasm_lib/justfile

@@ -0,0 +1,23 @@
+jfdir := replace(justfile_directory(), "\\", "/")
+doc_dist := jfdir / "target"/ "doc"
+crate_name := "ngvfs_init_wasm_lib"
+doc_json_filename := "ngvfs_init_wasm_lib.json"
+doc_json_filepath := doc_dist / doc_json_filename
+global_doc_dist := jfdir / ".." / ".." / "docs"
+
+default:
+	just --list
+
+doc_html:
+    cd {{jfdir}}; cargo rustdoc --lib --
+
+serv_doc addr:
+	SimpleHttpTestServer.exe -f {{doc_dist}} -b {{addr}}
+
+#doc_json:
+#    cd {{jfdir}}; cargo rustdoc --no-deps --lib -p ngvfs_init_wasm_lib -- -v -Z unstable-options --output-format json
+#    cp {{doc_json_filepath}}  {{global_doc_dist / doc_json_filename }}
+#
+#doc_json2:
+#    cd {{jfdir}}; rustdoc {{jfdir / "src" / "lib.rs" }} -v -Z unstable-options --output-format json
+#    cp {{doc_json_filepath}}  {{global_doc_dist / doc_json_filename }}

+ 161 - 0
init-wasm/openngvfs_init_wasm_lib/src/afs.rs

@@ -0,0 +1,161 @@
+use crate::error::{MountError, AfsFreeError, AfsMkdirError};
+use crate::raw_fn;
+
+/// Afs Instance
+pub struct AfsInstance (i32);
+
+impl AfsInstance {
+    /// Create a new AfsInstance from raw handle
+    pub fn new_from_handle(handle: i32) -> Self {
+        Self(handle)
+    }
+
+    /// Mount the AfsInstance to specified mount point
+    pub fn mount(&self, mnt_path: &str) -> Result<(), MountError> {
+        let r = unsafe {
+            raw_fn::vfs_mount(self.0, mnt_path.as_ptr(), mnt_path.len())
+        };
+        match r {
+            0 => Ok(()),
+            -1 => Err(MountError::InvalidAfsHandle),
+            -2 => Err(MountError::InternalError(
+                String::from("vfs_mount: reading MntPath from memory out of bounds")
+            )),
+            -3 => Err(MountError::MountPointAlreadyMounted),
+            -4 => Err(MountError::InternalError(
+                    String::from("vfs_mount: reading MntPath from memory out of bounds")
+            )),
+            _ => Err(MountError::InternalError(
+                format!("vfs_mount: unknown error: code = {}", r)
+            )),
+        }
+    }
+
+    /// Free the AfsInstance
+    /// The lifecycle of wasm_init is shorter than the lifecycle of the entire ngvfs,
+    /// so we can't utilize Rust's Drop trait to automatically release the AfsInstance object,
+    /// and you need to manually call free to release it if there is an unneeded AfsInstance object.
+    /// After this free function has been called, do not use this object again.
+    pub fn free(&self) -> Result<(), AfsFreeError> {
+        let r = unsafe {
+            raw_fn::afs_free(self.0)
+        };
+        match r {
+            0 => Ok(()),
+            -1 => Err(AfsFreeError::InvalidAfsHandle),
+            _ => Err(AfsFreeError::InternalError(
+                format!("vfs_free: unknown error: code = {}", r)
+            ))
+        }
+    }
+
+    pub fn get_handle_value(&self) -> i32 {
+        self.0
+    }
+
+    /// Create directory on AfsInstance. Works like `mkdir -p`.
+    pub fn mkdir(&self, path: &str, mode: i32) -> Result<(), AfsMkdirError> {
+        let r = unsafe {
+            raw_fn::afs_mkdir(self.0, path.as_ptr(), path.len(), mode)
+        };
+        match r {
+            0 => Ok(()),
+            -2 => Err(AfsMkdirError::InvalidAfsHandle),
+            -3 => Err(AfsMkdirError::InternalError(
+                String::from("afs_mkdir: reading path from memory out of bounds")
+            )),
+            -1 => Err(AfsMkdirError::InternalError(
+                String::from("afs_mkdir: failed create directory: see logs.")
+            )),
+            _ => Err(AfsMkdirError::InternalError(
+                format!("afs_mkdir: unknown error: code = {}", r)
+            )),
+        }
+    }
+
+    /// New afero.OsFs Instance
+    pub fn new_afero_osfs() -> Self {
+        let r = unsafe {
+            raw_fn::afs_create_osfs()
+        };
+        if r > 0 {
+            Self(r)
+        }else{
+            panic!("failed to create afero.OsFs: err_code={}", r)
+        }
+    }
+
+    /// New afero.MemFs Instance
+    pub fn new_afero_memfs() -> Self {
+        let r = unsafe {
+            raw_fn::afs_create_memfs()
+        };
+        if r > 0 {
+            Self(r)
+        }else{
+            panic!("failed to create afero.MemFs: err_code={}", r)
+        }
+    }
+
+    /// New afero.BasePathFs Instance.
+    pub fn new_afero_bpfs(base_fs: &AfsInstance, base_path: &str) -> Self {
+        let r = unsafe {
+            raw_fn::afs_create_bpfs(base_fs.0, base_path.as_ptr(), base_path.len())
+        };
+        if r > 0 {
+            Self(r)
+        }else{
+            panic!("failed to create afero.BasePathFs: err_code={}", r)
+        }
+    }
+
+    /// New afero.RegexpFs Instance
+    pub fn new_afero_regfs(base_fs: &AfsInstance, regexp: &str) -> Self {
+        let r = unsafe {
+            raw_fn::afs_create_regfs(base_fs.0, regexp.as_ptr(), regexp.len())
+        };
+        if r > 0 {
+            Self(r)
+        }else{
+            panic!("failed to create afero.RegexpFs: err_code={}", r)
+        }
+    }
+
+    /// New afero.ReadOnlyFs Instance
+    pub fn new_afero_rofs(base_fs: &AfsInstance) -> Self {
+        let r = unsafe {
+            raw_fn::afs_create_rofs(base_fs.0)
+        };
+        if r > 0 {
+            Self(r)
+        }else{
+            panic!("failed to create afero.ReadOnlyFs: err_code={}", r)
+        }
+    }
+
+    /// New afero.CopyOnWriteFs Instance
+    pub fn new_afero_cowfs(base_ro_fs: &AfsInstance, base_wr_fs: &AfsInstance) -> Self {
+        let r = unsafe {
+            raw_fn::afs_create_cowfs(base_ro_fs.0, base_wr_fs.0)
+        };
+        if r > 0 {
+            Self(r)
+        }else{
+            panic!("failed to create afero.CopyOnWriteFs: err_code={}", r)
+        }
+    }
+
+    /// New afero.CacheOnReadFs Instance. cacheSeconds is the cache time in seconds.
+    /// if cache_time_secs == 0, cache is permanent.
+    /// if cache_time_secs < 0, will cause error.
+    pub fn new_afero_corfs(base_ro_fs: &AfsInstance, base_wr_fs: &AfsInstance, cache_time_secs: i32) -> Self {
+        let r = unsafe {
+            raw_fn::afs_create_corfs(base_ro_fs.0, base_wr_fs.0, cache_time_secs)
+        };
+        if r > 0 {
+            Self(r)
+        }else{
+            panic!("failed to create afero.CacheOnReadFs: err_code={}", r)
+        }
+    }
+}

+ 58 - 0
init-wasm/openngvfs_init_wasm_lib/src/bep_ovfs.rs

@@ -0,0 +1,58 @@
+use crate::raw_fn;
+use crate::afs;
+use crate::afs::AfsInstance;
+use crate::error::{CreateBepOvfsError, MountError, AfsFreeError, AfsMkdirError};
+
+pub struct AfsBepOvFsInstance{
+    parent: afs::AfsInstance,
+}
+
+impl AfsBepOvFsInstance {
+    pub fn new(fs_layer_handles: &[&AfsInstance], writable: bool) -> Result<Self, CreateBepOvfsError>{
+        let mut afs_handle_vec = Vec::<i32>::new();
+        for i in fs_layer_handles {
+            afs_handle_vec.push(i.get_handle_value());
+        }
+        let afs_handle_slice = afs_handle_vec.as_slice();
+        let r = unsafe {
+            raw_fn::afs_create_bep_ovfs(
+                afs_handle_slice.as_ptr(),
+                afs_handle_slice.len(),
+                if writable { 1 } else { 0 },
+            )
+        };
+        match r {
+            v if v > 0 => Ok(Self {
+                parent: afs::AfsInstance::new_from_handle(v),
+            }),
+            -1 => Err(CreateBepOvfsError::EmptyFsLayerArray),
+            -2 => Err(CreateBepOvfsError::MemeoryReadOutOfBounds),
+            -3 => Err(CreateBepOvfsError::InvalidArrayElementLength),
+            -4 => Err(CreateBepOvfsError::InvalidHandle),
+            -5 => Err(CreateBepOvfsError::CreateAfsInstanceFailed),
+            _ => Err(CreateBepOvfsError::UnknownError(r))
+        }
+    }
+
+    pub fn new_from_handle(handle: i32) -> Self {
+        Self{
+            parent: afs::AfsInstance::new_from_handle(handle),
+        }
+    }
+
+    pub fn mount(&self, mnt_path: &str) -> Result<(), MountError>{
+        self.parent.mount(mnt_path)
+    }
+
+    pub fn free(&self) -> Result<(), AfsFreeError>{
+        self.parent.free()
+    }
+
+    pub fn get_handle_value(&self) -> i32{
+        self.parent.get_handle_value()
+    }
+
+    pub fn mkdir(&self, path: &str, mode: i32) -> Result<(), AfsMkdirError>{
+        self.parent.mkdir(path, mode)
+    }
+}

+ 93 - 0
init-wasm/openngvfs_init_wasm_lib/src/error.rs

@@ -0,0 +1,93 @@
+use std::fmt::Debug;
+
+pub enum GetKeyError {
+    KeyNotExists,
+    InternalError(String)
+}
+
+impl Debug for GetKeyError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            GetKeyError::KeyNotExists => write!(f, "KeyNotExists"),
+            GetKeyError::InternalError(v) => write!(f, "InternalError: {}", v),
+        }
+    }
+}
+
+pub enum MountError {
+    InvalidAfsHandle,
+    InternalError(String),
+    MountPointAlreadyMounted,
+}
+
+impl Debug for MountError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            MountError::InvalidAfsHandle => write!(f, "InvalidAfsHandle"),
+            MountError::InternalError(v) => write!(f, "InternalError: {}", v),
+            MountError::MountPointAlreadyMounted => write!(f, "MountPointAlreadyMounted"),
+        }
+    }
+}
+
+pub enum AfsFreeError {
+    InvalidAfsHandle,
+    InternalError(String),
+}
+
+impl Debug for AfsFreeError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            AfsFreeError::InvalidAfsHandle => write!(f, "InvalidAfsHandle"),
+            AfsFreeError::InternalError(v) => write!(f, "InternalError: {}", v),
+        }
+    }
+}
+
+pub enum MkdirError {
+    InternalError(String),
+}
+
+impl Debug for MkdirError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            MkdirError::InternalError(v) => write!(f, "InternalError: {}", v),
+        }
+    }
+}
+
+pub enum AfsMkdirError {
+    InvalidAfsHandle,
+    InternalError(String),
+}
+
+impl Debug for AfsMkdirError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            AfsMkdirError::InvalidAfsHandle => write!(f, "InvalidAfsHandle"),
+            AfsMkdirError::InternalError(v) => write!(f, "InternalError: {}", v),
+        }
+    }
+}
+
+pub enum CreateBepOvfsError {
+    CreateAfsInstanceFailed,
+    MemeoryReadOutOfBounds,
+    EmptyFsLayerArray,
+    InvalidArrayElementLength,
+    InvalidHandle,
+    UnknownError(i32),
+}
+
+impl Debug for CreateBepOvfsError {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            CreateBepOvfsError::CreateAfsInstanceFailed => write!(f, "CreateAfsInstanceFailed"),
+            CreateBepOvfsError::MemeoryReadOutOfBounds => write!(f, "MemeoryReadOutOfBounds"),
+            CreateBepOvfsError::EmptyFsLayerArray => write!(f, "EmptyFsLayerArray"),
+            CreateBepOvfsError::InvalidArrayElementLength => write!(f, "InvalidArrayElementLength"),
+            CreateBepOvfsError::InvalidHandle => write!(f, "InvalidHandle"),
+            CreateBepOvfsError::UnknownError(ec) => write!(f, "UnknownError: code={}", ec),
+        }
+    }
+}

+ 129 - 0
init-wasm/openngvfs_init_wasm_lib/src/lib.rs

@@ -0,0 +1,129 @@
+mod raw_fn;
+pub mod error;
+pub mod afs;
+pub mod bep_ovfs;
+
+use std::sync::OnceLock;
+use std::env;
+use crate::error::MkdirError;
+
+#[no_mangle]
+pub extern "C" fn auto_mount() -> i32 {
+    AUTO_MOUNT_HANDLER.get().unwrap()()
+}
+
+type AutoMountHandler = fn() -> i32;
+
+pub static AUTO_MOUNT_HANDLER: OnceLock<AutoMountHandler> = OnceLock::new();
+
+pub fn set_auto_mount_handler(handler: AutoMountHandler) {
+    AUTO_MOUNT_HANDLER.set(handler).unwrap();
+}
+
+/// Get API version
+/// returns the Major Version, Minor Version and Patch Version
+pub fn api_ver_get() -> (i32, i32, i32) {
+    let mut maj: i32 = 0;
+    let mut min: i32 = 0;
+    let mut pat: i32 = 0;
+    unsafe {
+        raw_fn::api_ver_get(&mut maj, &mut min, &mut pat);
+    }
+    (maj, min, pat)
+}
+
+/// Check if key exists in exkv
+pub fn exkv_has_key(key: &str) -> bool {
+    let ret = unsafe {
+        raw_fn::exkv_get_len(key.as_ptr(), key.len())
+    };
+    ret >= 0
+}
+
+/// Get value of key in exkv
+pub fn exkv_get(key: &str) ->Result<Vec<u8>, error::GetKeyError> {
+    let ret = unsafe {
+        raw_fn::exkv_get_len(key.as_ptr(), key.len())
+    };
+    let len: usize = match ret {
+        -2 => return Err(error::GetKeyError::KeyNotExists),
+        -1 => return Err(error::GetKeyError::InternalError(
+            String::from("exkv_get_len: reading key from memory out of bounds")
+        )),
+        v if v > 0 => v.try_into().unwrap(),
+        _ => return Err(error::GetKeyError::InternalError(
+            format!("exkv_get_len: unknown error: code = {}", ret)
+        )),
+    };
+    let mut buf = vec![0; len as usize];
+    let ret = unsafe {
+        raw_fn::exkv_get_val(key.as_ptr(), key.len(), buf.as_mut_ptr(), len)
+    };
+    match ret {
+        -3 => Err(error::GetKeyError::InternalError(
+            String::from("exkv_get_val: reading key from memory out of bounds")
+        )),
+        -2 => Err(error::GetKeyError::KeyNotExists),
+        -1 => Err(error::GetKeyError::InternalError(
+            String::from("exkv_get_val: writing value to buffer memory out of bounds")
+        )),
+        0 => Err(error::GetKeyError::InternalError(
+            String::from("exkv_get_val: buffer length <= 0")
+        )),
+        v if v >= 0 => if <i32 as TryInto<usize>>::try_into(v).unwrap() == len { Ok(buf) } else{
+            Err(error::GetKeyError::InternalError(
+                format!("exkv_get_val: length not match: value len={}, write to buffer {} bytes", len, v)
+            ))
+        },
+        _ => Err(error::GetKeyError::InternalError(
+            format!("exkv_get_val: unknown error: code = {}", ret)
+        ))
+    }
+}
+
+/// Create directory for prepare mount point. Works like `mkdir -p`.
+pub fn vfs_mkdir(path: &str, mode: i32) -> Result<(), MkdirError> {
+    let r = unsafe {
+        raw_fn::vfs_mkdir(path.as_ptr(), path.len(), mode)
+    };
+    match r {
+        0 => Ok(()),
+        -1 => Err(MkdirError::InternalError(
+            String::from("vfs_mkdir: reading path from memory out of bounds")
+        )),
+        -2 => Err(MkdirError::InternalError(
+            String::from("vfs_mkdir: failed create directory: see logs.")
+        )),
+        _ => Err(MkdirError::InternalError(
+            format!("vfs_mkdir: unknown error: code = {}", r)
+        )),
+    }
+}
+
+/// Get app name form env variable 'NAGAE_APP_NAME'
+pub fn get_app_name() -> String {
+    let key = "NAGAE_APP_NAME";
+    match env::var(key) {
+        Ok(val) => val,
+        Err(e) => match e {
+            std::env::VarError::NotPresent => String::new(),
+            std::env::VarError::NotUnicode(v) => {
+                panic!("env variable {} is not unicode: {:?}", key, v)
+            }
+        },
+    }
+}
+
+/// Get nagae rov root path from env variable 'NAGAE_ROV_ROOT'
+pub fn get_nagae_rov_root() -> String {
+    let key = "NAGAE_ROV_ROOT";
+    match env::var(key) {
+        Ok(val) => val,
+        Err(e) => match e {
+            std::env::VarError::NotPresent => String::new(),
+            std::env::VarError::NotUnicode(v) => {
+                panic!("env variable {} is not unicode: {:?}", key, v)
+            }
+        },
+    }
+}

+ 18 - 0
init-wasm/openngvfs_init_wasm_lib/src/raw_fn.rs

@@ -0,0 +1,18 @@
+#[link(wasm_import_module = "ngvfs_init")]
+extern "C" {
+    pub fn api_ver_get(maj: *mut i32, min: *mut i32, max: *mut i32);
+    pub fn exkv_get_len(key: *const u8, lenKey: usize) -> i32;
+    pub fn exkv_get_val(key: *const u8, lenKey: usize, ptrBuf: *mut u8, lenBuf: usize) -> i32;
+    pub fn vfs_mount(hAfs: i32, ptrMntPath: *const u8, lenMntPath: usize) -> i32;
+    pub fn vfs_mkdir(ptrMntPath: *const u8, lenMntPath: usize, mode: i32) -> i32;
+    pub fn afs_free(hAfs: i32) -> i32;
+    pub fn afs_mkdir(hAfs: i32, ptrPath: *const u8, lenPath: usize, mode: i32) -> i32;
+    pub fn afs_create_osfs() -> i32;
+    pub fn afs_create_memfs() -> i32;
+    pub fn afs_create_bpfs(hAfs: i32, ptrBasePath: *const u8, lenBasePath: usize) -> i32;
+    pub fn afs_create_regfs(hAfs: i32, ptrRegExp: *const u8, lenRegExp: usize) -> i32;
+    pub fn afs_create_rofs(hAfs: i32) -> i32;
+    pub fn afs_create_cowfs(hRoAfs: i32, hWrAfs: i32) -> i32;
+    pub fn afs_create_corfs(hRoAfs: i32, hWrAfs: i32, cacheTime: i32) -> i32;
+    pub fn afs_create_bep_ovfs(ptrFsLayerArray: *const i32, lenFsLayerArray: usize, bWritable: i32) -> i32;
+}

+ 109 - 0
openngvfs/fs_impl.go

@@ -0,0 +1,109 @@
+package openngvfs
+
+import (
+	"github.com/spf13/afero"
+	"os"
+	"time"
+)
+
+var _ afero.Fs = (*OpenNagaeVFS)(nil)
+
+func (n *OpenNagaeVFS) Create(name string) (afero.File, error) {
+	f, err := n.root.Create(name)
+	if err != nil {
+		return nil, n.errorProcess(name, "create", err)
+	}
+	return f, nil
+}
+
+func (n *OpenNagaeVFS) Mkdir(name string, perm os.FileMode) error {
+	err := n.root.Mkdir(name, perm)
+	if err != nil {
+		return n.errorProcess(name, "create", err)
+	}
+	return nil
+}
+
+func (n *OpenNagaeVFS) Name() string {
+	return "nagae-vfs"
+}
+
+func (n *OpenNagaeVFS) MkdirAll(path string, perm os.FileMode) error {
+	err := n.root.MkdirAll(path, perm)
+	if err != nil {
+		return n.errorProcess(path, "mkdirall", err)
+	}
+	return nil
+}
+
+func (n *OpenNagaeVFS) Open(name string) (afero.File, error) {
+	f, err := n.root.Open(name)
+	if err != nil {
+		return nil, n.errorProcess(name, "open", err)
+	}
+	return f, nil
+}
+
+func (n *OpenNagaeVFS) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
+	f, err := n.root.OpenFile(name, flag, perm)
+	if err != nil {
+		return nil, n.errorProcess(name, "openfile", err)
+	}
+	return f, nil
+}
+
+func (n *OpenNagaeVFS) Remove(name string) error {
+	err := n.root.Remove(name)
+	if err != nil {
+		return n.errorProcess(name, "remove", err)
+	}
+	return nil
+}
+
+func (n *OpenNagaeVFS) RemoveAll(path string) error {
+	err := n.root.RemoveAll(path)
+	if err != nil {
+		return n.errorProcess(path, "removeall", err)
+	}
+	return nil
+}
+
+func (n *OpenNagaeVFS) Rename(oldname, newname string) error {
+	err := n.root.Rename(oldname, newname)
+	if err != nil {
+		return n.errorProcess(oldname, "rename", err)
+	}
+	return nil
+}
+
+func (n *OpenNagaeVFS) Stat(name string) (os.FileInfo, error) {
+	info, err := n.root.Stat(name)
+	if err != nil {
+		return nil, n.errorProcess(name, "stat", err)
+	}
+	return info, nil
+}
+
+func (n *OpenNagaeVFS) Chmod(name string, mode os.FileMode) error {
+	err := n.root.Chmod(name, mode)
+	if err != nil {
+		return n.errorProcess(name, "chmod", err)
+	}
+	return nil
+}
+
+func (n *OpenNagaeVFS) Chown(name string, uid, gid int) error {
+	err := n.root.Chown(name, uid, gid)
+	if err != nil {
+		return n.errorProcess(name, "chown", err)
+	}
+	return nil
+}
+
+func (n *OpenNagaeVFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
+	err := n.root.Chtimes(name, atime, mtime)
+	if err != nil {
+		return n.errorProcess(name, "chtimes", err)
+	}
+	return nil
+}

+ 35 - 0
openngvfs/fstab.go

@@ -0,0 +1,35 @@
+package openngvfs
+
+import (
+	"fmt"
+	"github.com/BurntSushi/toml"
+	"os"
+)
+
+type RawFsTab struct {
+	Wasm struct {
+		Default string            `toml:"default"`
+		App     map[string]string `toml:"app"`
+	} `toml:"wasm"`
+	ExKV struct {
+		Default map[string]string            `toml:"default"`
+		App     map[string]map[string]string `toml:"app"`
+	} `toml:"exkv"`
+}
+
+func loadFsTab(path string) (*RawFsTab, error) {
+	var fstab RawFsTab
+	ftBin, err := os.ReadFile(path)
+	if err != nil {
+		return nil, fmt.Errorf("failed read fstab.toml: %w", err)
+	}
+	err = toml.Unmarshal(ftBin, &fstab)
+	if err != nil {
+		return nil, fmt.Errorf("failed parse fstab.toml: %w", err)
+	}
+	return &fstab, nil
+}
+
+type ProcessedFsTab struct {
+	ExKV map[string]string
+}

+ 23 - 0
openngvfs/go.mod

@@ -0,0 +1,23 @@
+module git.swzry.com/zry/openNGVFS/openngvfs
+
+go 1.21.6
+
+require (
+	git.swzry.com/ProjectNagae/FsUtils/amntfs v0.0.0-20240311024254-32c411300be1
+	git.swzry.com/zry/GoHiedaLogger/hiedalog v0.0.0-20230814164330-c2545e8bfba1
+	git.swzry.com/zry/go-int32-handle v1.0.0
+	git.swzry.com/zry/io_writer_to_logger v0.0.0-20240311070728-027ba1ed43cf
+	git.swzry.com/zry/pathutils v0.0.0-20221111111354-c1a495112ba9
+	github.com/BurntSushi/toml v1.3.2
+	github.com/bep/overlayfs v0.9.2
+	github.com/spf13/afero v1.11.0
+	github.com/tetratelabs/wazero v1.6.0
+)
+
+require (
+	git.swzry.com/ProjectNagae/FsUtils/mountree v0.0.0-20240311024254-32c411300be1 // indirect
+	github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f // indirect
+	github.com/gammazero/deque v0.2.1 // indirect
+	github.com/golang/glog v1.2.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+)

+ 41 - 0
openngvfs/go.sum

@@ -0,0 +1,41 @@
+git.swzry.com/ProjectNagae/FsUtils/amntfs v0.0.0-20240311024254-32c411300be1 h1:804S+25K6cIwnkBgSlwWR28iomf2gQ1vGNtJAecNPoU=
+git.swzry.com/ProjectNagae/FsUtils/amntfs v0.0.0-20240311024254-32c411300be1/go.mod h1:vAOlO1iZVkalFq1a5GrwIkJiTZR2hC+KB9eVeVzE2t8=
+git.swzry.com/ProjectNagae/FsUtils/mountree v0.0.0-20240311024254-32c411300be1 h1:JkJAnU9QD43jL+TLR6JOY1EO3uN6UW8kyxO7gCpowQc=
+git.swzry.com/ProjectNagae/FsUtils/mountree v0.0.0-20240311024254-32c411300be1/go.mod h1:k7qCoLuuMmOdJIuW7qAqu3j65CtKmPKga88WZ0EC8rc=
+git.swzry.com/zry/GoHiedaLogger/hiedalog v0.0.0-20230814164330-c2545e8bfba1 h1:5lcgeoBT7tvU/XH9jKlzQguiYjLbdh7SY4UsUf/Ek00=
+git.swzry.com/zry/GoHiedaLogger/hiedalog v0.0.0-20230814164330-c2545e8bfba1/go.mod h1:NMU7558kNXCUuK0qKYQMtYK/kn2lhwelnij295H3pdU=
+git.swzry.com/zry/go-int32-handle v1.0.0 h1:zEGSbe5rnaPsXDdrPZYSLKIRgouw91fsipgd/sYuJNk=
+git.swzry.com/zry/go-int32-handle v1.0.0/go.mod h1:HrOTfg52vhfStCvtKmINg3Vvz2aivdOEkNgOltvhFbg=
+git.swzry.com/zry/io_writer_to_logger v0.0.0-20240311070728-027ba1ed43cf h1:JdhCaLpZCxdPU1pLKi2OBxnDwoKsXDi1cSBcNGz85nY=
+git.swzry.com/zry/io_writer_to_logger v0.0.0-20240311070728-027ba1ed43cf/go.mod h1:Cgg0ps76kAkDt2Hh0CIosSRH1DUe4XKw3Nk37GGE1dE=
+git.swzry.com/zry/pathutils v0.0.0-20221111111354-c1a495112ba9 h1:QgQYkCcmA3hJa2SD54i5VUruKf7IO4pzhPiSaml/zjI=
+git.swzry.com/zry/pathutils v0.0.0-20221111111354-c1a495112ba9/go.mod h1:BhIO8/5pXEldM0+n3TvdRguR4cfD7Th6YwdTdu7p508=
+github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
+github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f h1:RDkg3pyE1qGbBpRWmvSN9RNZC5nUrOaEPiEpEb8y2f0=
+github.com/GUAIK-ORG/go-snowflake v0.0.0-20200116064823-220c4260e85f/go.mod h1:zA7AF9RTfpluCfz0omI4t5KCMaWHUMicsZoMccnaT44=
+github.com/bep/overlayfs v0.9.2 h1:qJEmFInsW12L7WW7dOTUhnMfyk/fN9OCDEO5Gr8HSDs=
+github.com/bep/overlayfs v0.9.2/go.mod h1:aYY9W7aXQsGcA7V9x/pzeR8LjEgIxbtisZm8Q7zPz40=
+github.com/frankban/quicktest v1.14.2 h1:SPb1KFFmM+ybpEjPUhCCkZOM5xlovT5UbrMvWnXyBns=
+github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
+github.com/gammazero/deque v0.2.1 h1:qSdsbG6pgp6nL7A0+K/B7s12mcCY/5l5SIUpMOl+dC0=
+github.com/gammazero/deque v0.2.1/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
+github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
+github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
+github.com/tetratelabs/wazero v1.6.0 h1:z0H1iikCdP8t+q341xqepY4EWvHEw8Es7tlqiVzlP3g=
+github.com/tetratelabs/wazero v1.6.0/go.mod h1:0U0G41+ochRKoPKCJlh0jMg1CHkyfK8kDqiirMmKY8A=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=

+ 92 - 0
openngvfs/hieda_adapter.go

@@ -0,0 +1,92 @@
+package openngvfs
+
+import (
+	"fmt"
+	"git.swzry.com/zry/GoHiedaLogger/hiedalog"
+	"git.swzry.com/zry/io_writer_to_logger"
+	"io"
+)
+
+var _ LogHandlerIntf = (*LoggerHiedaLogAdapter)(nil)
+
+type LoggerHiedaLogAdapter struct {
+	logger *hiedalog.HiedaLogger
+}
+
+func (e LoggerHiedaLogAdapter) FsInitWasm(name string) {
+	e.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+		"type": "will_init_by_wasm",
+		"wasm": name,
+	})
+}
+
+func (e LoggerHiedaLogAdapter) ChildFsError(mid int64, fstype, mountpoint, path, action string, rawErr error) {
+	e.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+		"type":    "child_fs_error",
+		"path":    path,
+		"fstype":  fstype,
+		"action":  action,
+		"mnt":     mountpoint,
+		"mid":     fmt.Sprintf("%016X", mid),
+		"raw_err": rawErr.Error(),
+	})
+}
+
+func (e LoggerHiedaLogAdapter) NoAvailableMountPointForThisPath(path string, action string) {
+	e.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+		"type":   "no_mnt_point_error",
+		"path":   path,
+		"action": action,
+	})
+}
+
+func (e LoggerHiedaLogAdapter) InitError(err error) {
+	e.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+		"type":    "ngvfs_init_error",
+		"raw_err": err.Error(),
+	})
+}
+
+func (e LoggerHiedaLogAdapter) UnknownError(path string, action string, err error) {
+	e.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+		"type":    "unknown_error",
+		"path":    path,
+		"action":  action,
+		"raw_err": err.Error(),
+	})
+}
+
+var _ io.WriteCloser = (*IOWriterAdapter)(nil)
+
+type IOWriterAdapter struct {
+	logger *hiedalog.HiedaLogger
+	iowr   *io_writer_to_logger.WriterLogWrapper
+	module string
+	level  string
+}
+
+func (a IOWriterAdapter) Write(p []byte) (n int, err error) {
+	return a.iowr.Write(p)
+}
+
+func (a IOWriterAdapter) Close() error {
+	return a.iowr.Close()
+}
+
+func (a IOWriterAdapter) Run() {
+	a.iowr.Run()
+}
+
+func (a IOWriterAdapter) log(msg string) {
+	a.logger.LogString(a.module, a.level, msg)
+}
+
+func NewIOWriterAdapter(logger *hiedalog.HiedaLogger, module string, level string) *IOWriterAdapter {
+	a := &IOWriterAdapter{
+		logger: logger,
+		module: module,
+		level:  level,
+	}
+	a.iowr = io_writer_to_logger.NewWriterLogWrapper(a.log)
+	return a
+}

+ 107 - 0
openngvfs/ngvfs.go

@@ -0,0 +1,107 @@
+package openngvfs
+
+import (
+	"context"
+	"fmt"
+	"git.swzry.com/ProjectNagae/FsUtils/amntfs"
+	"git.swzry.com/zry/GoHiedaLogger/hiedalog"
+)
+
+type LogHandlerIntf interface {
+	ChildFsError(mid int64, fstype, mountpoint, path, action string, rawErr error)
+	NoAvailableMountPointForThisPath(path string, action string)
+	InitError(err error)
+	UnknownError(path string, action string, err error)
+	FsInitWasm(name string)
+}
+
+type OpenNagaeVFS struct {
+	root     *amntfs.AMNTFS
+	logger   *hiedalog.HiedaLogger
+	initWAVM *InitWAVM
+}
+
+func NewOpenNagaeVFS(logger *hiedalog.HiedaLogger) *OpenNagaeVFS {
+	return &OpenNagaeVFS{
+		root:   amntfs.NewAMNTFS(),
+		logger: logger,
+	}
+}
+
+func (n *OpenNagaeVFS) errorProcess(path, action string, err error) error {
+	if aErr, ok := err.(*amntfs.AMNTFSError); ok {
+		rawErr := aErr.GetRawError()
+		var rawErrStr string
+		if rawErr != nil {
+			rawErrStr = rawErr.Error()
+		} else {
+			rawErrStr = ""
+		}
+		switch aErr.GetErrNo() {
+		case amntfs.ErrChildFsError:
+			n.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type":    "child_fs_error",
+				"path":    path,
+				"fstype":  aErr.GetFsType(),
+				"action":  action,
+				"mnt":     aErr.GetMountPoint(),
+				"mid":     fmt.Sprintf("%016X", aErr.GetMountId()),
+				"raw_err": rawErrStr,
+			})
+			return rawErr
+		case amntfs.ErrNoAvailableMountPointForThisPath:
+			n.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type":   "no_mnt_point_error",
+				"path":   path,
+				"action": action,
+			})
+			return fmt.Errorf("no available mount point for path '%s'", path)
+		default:
+			n.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type":    "unknown_error",
+				"path":    path,
+				"action":  action,
+				"raw_err": err.Error(),
+			})
+			return err
+		}
+	} else {
+		var xerrStr string
+		if err != nil {
+			xerrStr = err.Error()
+		} else {
+			xerrStr = ""
+		}
+		n.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+			"type":    "unknown_error",
+			"path":    path,
+			"action":  action,
+			"raw_err": xerrStr,
+		})
+	}
+	return err
+}
+
+func (n *OpenNagaeVFS) Init(ctx context.Context, nagaeRootEnv string, appName string) error {
+	hmApi := NewWAVMHostAPI(n.root)
+	vm, err := NewInitWAVM(ctx, hmApi, nagaeRootEnv, appName, n.logger)
+	if err != nil {
+		return fmt.Errorf("failed to init ngvfs: %w", err)
+	}
+	n.initWAVM = vm
+	return n.initWAVM.Init()
+}
+
+func (n *OpenNagaeVFS) DeInit() error {
+	if n.initWAVM == nil {
+		return fmt.Errorf("deinit before init")
+	}
+	return n.initWAVM.DeInit()
+}
+
+func (n *OpenNagaeVFS) RunMount() error {
+	if n.initWAVM == nil {
+		return fmt.Errorf("deinit before init")
+	}
+	return n.initWAVM.AutoMount()
+}

+ 269 - 0
openngvfs/wa_init_api.go

@@ -0,0 +1,269 @@
+package openngvfs
+
+import (
+	"context"
+	"fmt"
+	"git.swzry.com/ProjectNagae/FsUtils/amntfs"
+	"git.swzry.com/zry/GoHiedaLogger/hiedalog"
+	"git.swzry.com/zry/go-int32-handle"
+	"github.com/spf13/afero"
+	"github.com/tetratelabs/wazero"
+	wa_api "github.com/tetratelabs/wazero/api"
+	"os"
+)
+
+const API_VERSION_MAJOR = 1
+const API_VERSION_MINOR = 1
+const API_VERSION_PATCH = 0
+
+type AferoFsInstance struct {
+	Fs              afero.Fs
+	TypeDescription string
+}
+
+type InitWAVMHostAPI struct {
+	Root                *amntfs.AMNTFS
+	AferoFsInstancesMgr *go_int32_handle.HandleManager[*AferoFsInstance]
+}
+
+func NewWAVMHostAPI(root *amntfs.AMNTFS) *InitWAVMHostAPI {
+	return &InitWAVMHostAPI{
+		Root:                root,
+		AferoFsInstancesMgr: go_int32_handle.New[*AferoFsInstance](),
+	}
+}
+
+type InitWAVMHostModuleAdapter struct {
+	api            *InitWAVMHostAPI
+	builder        wazero.HostModuleBuilder
+	rawFsTab       *RawFsTab
+	processedFsTab *ProcessedFsTab
+	logger         *hiedalog.HiedaLogger
+}
+
+func NewInitWAVMHostModuleAdapter(builder wazero.HostModuleBuilder, api *InitWAVMHostAPI, fstab *RawFsTab, logger *hiedalog.HiedaLogger, appName string) *InitWAVMHostModuleAdapter {
+	localKV := make(map[string]string)
+	for k, v := range fstab.ExKV.Default {
+		localKV[k] = v
+	}
+	appKV, ok := fstab.ExKV.App[appName]
+	if ok {
+		for k, v := range appKV {
+			localKV[k] = v
+		}
+	}
+	processedFsTab := &ProcessedFsTab{
+		ExKV: localKV,
+	}
+	return &InitWAVMHostModuleAdapter{
+		api:            api,
+		builder:        builder,
+		rawFsTab:       fstab,
+		processedFsTab: processedFsTab,
+		logger:         logger,
+	}
+}
+
+func (a *InitWAVMHostModuleAdapter) addFunc(
+	name string, f func(ctx context.Context, mod wa_api.Module, params []uint64),
+	param []wa_api.ValueType, result []wa_api.ValueType,
+) {
+	a.builder = a.builder.NewFunctionBuilder().
+		WithGoModuleFunction(wa_api.GoModuleFunc(f), param, result).Export(name)
+}
+
+func (a *InitWAVMHostModuleAdapter) Instantiate(ctx context.Context) (wa_api.Module, error) {
+	a.addFunc("api_ver_get", a.efnApiVerGet,
+		[]wa_api.ValueType{
+			wa_api.ValueTypeI32, wa_api.ValueTypeI32, wa_api.ValueTypeI32,
+		}, []wa_api.ValueType{})
+	a.addFunc("exkv_get_len", a.efnExkvGetLen,
+		[]wa_api.ValueType{wa_api.ValueTypeI32, wa_api.ValueTypeI32},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("exkv_get_val", a.efnExkvGetVal,
+		[]wa_api.ValueType{
+			wa_api.ValueTypeI32, wa_api.ValueTypeI32,
+			wa_api.ValueTypeI32, wa_api.ValueTypeI32,
+		},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("vfs_mount", a.efnVfsMount,
+		[]wa_api.ValueType{
+			wa_api.ValueTypeI32, wa_api.ValueTypeI32, wa_api.ValueTypeI32,
+		},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("vfs_mkdir", a.efnVfsMkdir,
+		[]wa_api.ValueType{
+			wa_api.ValueTypeI32, wa_api.ValueTypeI32, wa_api.ValueTypeI32,
+		},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.mfaAddAfs()
+	return a.builder.Instantiate(ctx)
+}
+
+func (a *InitWAVMHostModuleAdapter) efnApiVerGet(_ context.Context, mod wa_api.Module, stack []uint64) {
+	ptrMaj := uint32(stack[0])
+	ptrMin := uint32(stack[1])
+	ptrPat := uint32(stack[2])
+	mod.Memory().WriteUint32Le(ptrMaj, API_VERSION_MAJOR)
+	mod.Memory().WriteUint32Le(ptrMin, API_VERSION_MINOR)
+	mod.Memory().WriteUint32Le(ptrPat, API_VERSION_PATCH)
+}
+
+func (a *InitWAVMHostModuleAdapter) efnExkvGetLen(_ context.Context, mod wa_api.Module, stack []uint64) {
+	ptrKey := uint32(stack[0])
+	lenKey := uint32(stack[1])
+	key, ok := mod.Memory().Read(ptrKey, lenKey)
+	if !ok {
+		var neg1 int32 = -1
+		stack[0] = uint64(neg1)
+		return
+	}
+	v, ok := a.processedFsTab.ExKV[string(key)]
+	if !ok {
+		var neg2 int32 = -2
+		stack[0] = uint64(neg2)
+	} else {
+		stack[0] = uint64(len([]byte(v)))
+	}
+}
+
+func (a *InitWAVMHostModuleAdapter) efnExkvGetVal(_ context.Context, mod wa_api.Module, stack []uint64) {
+	ptrKey := uint32(stack[0])
+	lenKey := uint32(stack[1])
+	ptrBuf := uint32(stack[2])
+	lenBuf := uint32(stack[3])
+	if lenBuf <= 0 {
+		stack[0] = 0
+		return
+	}
+	key, ok := mod.Memory().Read(ptrKey, lenKey)
+	if !ok {
+		var neg1 int32 = -1
+		stack[0] = uint64(neg1)
+		return
+	}
+	v, ok := a.processedFsTab.ExKV[string(key)]
+	if !ok {
+		var neg2 int32 = -2
+		stack[0] = uint64(neg2)
+		return
+	}
+	vb := []byte(v)
+	ok = mod.Memory().Write(ptrBuf, vb[:lenBuf])
+	if !ok {
+		var neg3 int32 = -3
+		stack[0] = uint64(neg3)
+		return
+	}
+	stack[0] = uint64(lenBuf)
+	return
+}
+
+func (a *InitWAVMHostModuleAdapter) efnVfsMount(_ context.Context, mod wa_api.Module, stack []uint64) {
+	hAfs := int32(stack[0])
+	ptrMntPath := uint32(stack[1])
+	lenMntPath := uint32(stack[2])
+	mntPath, ok := mod.Memory().Read(ptrMntPath, lenMntPath)
+	if !ok {
+		var neg2 int32 = -2
+		stack[0] = uint64(neg2)
+		return
+	}
+	if hAfs <= 0 {
+		var neg1 int32 = -1
+		stack[0] = uint64(neg1)
+		return
+	}
+	objAfs, ok := a.api.AferoFsInstancesMgr.Get(hAfs)
+	if !ok {
+		var neg1 int32 = -1
+		stack[0] = uint64(neg1)
+		return
+	}
+	err := a.api.Root.Mount(string(mntPath), objAfs.Fs, false)
+	if err != nil {
+		if aErr, ok := err.(*amntfs.AMNTFSError); ok {
+			rawErr := aErr.GetRawError()
+			var rawErrStr string
+			if rawErr != nil {
+				rawErrStr = rawErr.Error()
+			} else {
+				rawErrStr = ""
+			}
+			if aErr.GetErrNo() == amntfs.ErrMountPointAlreadyMounted {
+				a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+					"type":          "mount_error",
+					"sub-type":      "mount_point_already_mounted",
+					"mnt-point":     string(mntPath),
+					"afs-name":      objAfs.Fs.Name(),
+					"afs-type-desc": objAfs.TypeDescription,
+				})
+				var neg3 int32 = -3
+				stack[0] = uint64(neg3)
+				return
+			} else {
+				a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+					"type":          "mount_error",
+					"sub-type":      "internal_error",
+					"mnt-point":     string(mntPath),
+					"afs-name":      objAfs.Fs.Name(),
+					"afs-type-desc": objAfs.TypeDescription,
+					"raw-err":       rawErrStr,
+				})
+				var neg4 int32 = -4
+				stack[0] = uint64(neg4)
+				return
+			}
+		} else {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type":          "mount_error",
+				"sub-type":      "internal_error",
+				"mnt-point":     string(mntPath),
+				"afs-name":      objAfs.Fs.Name(),
+				"afs-type-desc": objAfs.TypeDescription,
+				"raw-err":       err.Error(),
+			})
+			var neg4 int32 = -4
+			stack[0] = uint64(neg4)
+			return
+		}
+	}
+	a.logger.LogComplex("ngvfs", hiedalog.DLN_VERBOSE, map[string]string{
+		"type":          "mount_ok",
+		"mnt-point":     string(mntPath),
+		"afs-name":      objAfs.Fs.Name(),
+		"afs-type-desc": objAfs.TypeDescription,
+	})
+	stack[0] = 0
+	return
+}
+
+func (a *InitWAVMHostModuleAdapter) efnVfsMkdir(_ context.Context, mod wa_api.Module, stack []uint64) {
+	ptrPath := uint32(stack[0])
+	lenPath := uint32(stack[1])
+	perm := uint32(stack[2])
+	dirPath, ok := mod.Memory().Read(ptrPath, lenPath)
+	if !ok {
+		var neg2 int32 = -1
+		stack[0] = uint64(neg2)
+		return
+	}
+	err := a.api.Root.MkdirAll(string(dirPath), os.FileMode(perm))
+	if err != nil {
+		a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+			"type":     "vfs_mkdir_error",
+			"dir-path": string(dirPath),
+			"perm":     fmt.Sprintf("%04o", perm),
+			"raw-err":  err.Error(),
+		})
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	a.logger.LogComplex("ngvfs", hiedalog.DLN_VERBOSE, map[string]string{
+		"type": "vfs_mkdir_ok",
+		"path": string(dirPath),
+	})
+	stack[0] = 0
+	return
+}

+ 591 - 0
openngvfs/wa_init_api_afs.go

@@ -0,0 +1,591 @@
+package openngvfs
+
+import (
+	"context"
+	"encoding/binary"
+	"fmt"
+	"git.swzry.com/zry/GoHiedaLogger/hiedalog"
+	go_int32_handle "git.swzry.com/zry/go-int32-handle"
+	"github.com/bep/overlayfs"
+	"github.com/spf13/afero"
+	wa_api "github.com/tetratelabs/wazero/api"
+	"os"
+	"regexp"
+	"time"
+)
+
+func (a *InitWAVMHostModuleAdapter) mfaAddAfs() {
+	a.addFunc("afs_free", a.efnAfsFree,
+		[]wa_api.ValueType{wa_api.ValueTypeI32},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("afs_mkdir", a.efnAfsMkdir,
+		[]wa_api.ValueType{
+			wa_api.ValueTypeI32, wa_api.ValueTypeI32,
+			wa_api.ValueTypeI32, wa_api.ValueTypeI32,
+		},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("afs_create_osfs", a.efnAfsCreateOsFs,
+		[]wa_api.ValueType{},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("afs_create_memfs", a.efnAfsCreateMemFs,
+		[]wa_api.ValueType{},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("afs_create_bpfs", a.efnAfsCreateBpFs,
+		[]wa_api.ValueType{wa_api.ValueTypeI32, wa_api.ValueTypeI32, wa_api.ValueTypeI32},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("afs_create_regfs", a.efnAfsCreateRegFs,
+		[]wa_api.ValueType{wa_api.ValueTypeI32, wa_api.ValueTypeI32, wa_api.ValueTypeI32},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("afs_create_rofs", a.efnAfsCreateRoFs,
+		[]wa_api.ValueType{wa_api.ValueTypeI32},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("afs_create_cowfs", a.efnAfsCreateCowFs,
+		[]wa_api.ValueType{wa_api.ValueTypeI32, wa_api.ValueTypeI32},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("afs_create_corfs", a.efnAfsCreateCorFs,
+		[]wa_api.ValueType{wa_api.ValueTypeI32, wa_api.ValueTypeI32, wa_api.ValueTypeI32},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+	a.addFunc("afs_create_bep_ovfs", a.efnAfsCreateBepOvFs,
+		[]wa_api.ValueType{wa_api.ValueTypeI32, wa_api.ValueTypeI32, wa_api.ValueTypeI32},
+		[]wa_api.ValueType{wa_api.ValueTypeI32})
+}
+func (a *InitWAVMHostModuleAdapter) efnAfsFree(_ context.Context, _ wa_api.Module, stack []uint64) {
+	hAfs := int32(stack[0])
+	if hAfs <= 0 {
+		var neg1 int32 = -1
+		stack[0] = uint64(neg1)
+		return
+	}
+	_, ok := a.api.AferoFsInstancesMgr.Release(hAfs)
+	if !ok {
+		a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+			"type":   "afs_release_error",
+			"handle": fmt.Sprintf("%d", hAfs),
+		})
+		var neg1 int32 = -1
+		stack[0] = uint64(neg1)
+		return
+	}
+	a.logger.LogComplex("ngvfs-debug", hiedalog.DLN_DEBUG, map[string]string{
+		"type":   "afs_free_ok",
+		"handle": fmt.Sprintf("%d", hAfs),
+	})
+	stack[0] = 0
+	return
+}
+
+func (a *InitWAVMHostModuleAdapter) efnAfsMkdir(_ context.Context, mod wa_api.Module, stack []uint64) {
+	hAfs := int32(stack[0])
+	ptrPath := uint32(stack[1])
+	lenPath := uint32(stack[2])
+	perm := uint32(stack[3])
+	dirPath, ok := mod.Memory().Read(ptrPath, lenPath)
+	if !ok {
+		var ecode int32 = -3
+		stack[0] = uint64(ecode)
+		return
+	}
+	if hAfs <= 0 {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	objAfs, ok := a.api.AferoFsInstancesMgr.Get(hAfs)
+	if !ok {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	err := objAfs.Fs.MkdirAll(string(dirPath), os.FileMode(perm))
+	if err != nil {
+		a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+			"type":     "afs_mkdir_error",
+			"dir-path": string(dirPath),
+			"afs":      fmt.Sprintf("%d", hAfs),
+			"perm":     fmt.Sprintf("%04o", perm),
+			"raw-err":  err.Error(),
+		})
+		var ecode int32 = -1
+		stack[0] = uint64(ecode)
+		return
+	}
+	a.logger.LogComplex("ngvfs", hiedalog.DLN_VERBOSE, map[string]string{
+		"type": "afs_mkdir_ok",
+		"afs":  fmt.Sprintf("%d", hAfs),
+		"path": string(dirPath),
+	})
+	stack[0] = 0
+	return
+}
+
+func (a *InitWAVMHostModuleAdapter) efnAfsCreateOsFs(_ context.Context, _ wa_api.Module, stack []uint64) {
+	osfs := afero.NewOsFs()
+	afsObj := &AferoFsInstance{
+		Fs:              osfs,
+		TypeDescription: "afero-osfs",
+	}
+	h, err := a.api.AferoFsInstancesMgr.AllocateAndPut(afsObj)
+	if err != nil {
+		if err == go_int32_handle.ErrHandleExceedMax {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  "handle exceed max",
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		} else {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  err.Error(),
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		}
+	}
+	a.logger.LogComplex("ngvfs-debug", hiedalog.DLN_DEBUG, map[string]string{
+		"type":   "afs_create_osfs_ok",
+		"handle": fmt.Sprintf("%d", h),
+	})
+	stack[0] = uint64(h)
+	return
+}
+func (a *InitWAVMHostModuleAdapter) efnAfsCreateMemFs(_ context.Context, _ wa_api.Module, stack []uint64) {
+	memfs := afero.NewMemMapFs()
+	afsObj := &AferoFsInstance{
+		Fs:              memfs,
+		TypeDescription: "afero-memfs",
+	}
+	h, err := a.api.AferoFsInstancesMgr.AllocateAndPut(afsObj)
+	if err != nil {
+		if err == go_int32_handle.ErrHandleExceedMax {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  "handle exceed max",
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		} else {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  err.Error(),
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		}
+	}
+	a.logger.LogComplex("ngvfs-debug", hiedalog.DLN_DEBUG, map[string]string{
+		"type":   "afs_create_memfs_ok",
+		"handle": fmt.Sprintf("%d", h),
+	})
+	stack[0] = uint64(h)
+	return
+}
+
+func (a *InitWAVMHostModuleAdapter) efnAfsCreateBpFs(_ context.Context, mod wa_api.Module, stack []uint64) {
+	hBaseAfs := int32(stack[0])
+	ptrBasePath := uint32(stack[1])
+	lenBasePath := uint32(stack[2])
+	basePath, ok := mod.Memory().Read(ptrBasePath, lenBasePath)
+	if !ok {
+		var ecode int32 = -3
+		stack[0] = uint64(ecode)
+		return
+	}
+	if hBaseAfs <= 0 {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	objBaseAfs, ok := a.api.AferoFsInstancesMgr.Get(hBaseAfs)
+	if !ok {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	bpfs := afero.NewBasePathFs(objBaseAfs.Fs, string(basePath))
+	afsObj := &AferoFsInstance{
+		Fs:              bpfs,
+		TypeDescription: "afero-base-path-fs",
+	}
+	h, err := a.api.AferoFsInstancesMgr.AllocateAndPut(afsObj)
+	if err != nil {
+		if err == go_int32_handle.ErrHandleExceedMax {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  "handle exceed max",
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		} else {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  err.Error(),
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		}
+	}
+	a.logger.LogComplex("ngvfs-debug", hiedalog.DLN_DEBUG, map[string]string{
+		"type":      "afs_create_bpfs_ok",
+		"handle":    fmt.Sprintf("%d", h),
+		"base-afs":  fmt.Sprintf("%d", hBaseAfs),
+		"base-path": string(basePath),
+	})
+	stack[0] = uint64(h)
+	return
+}
+
+func (a *InitWAVMHostModuleAdapter) efnAfsCreateRegFs(_ context.Context, mod wa_api.Module, stack []uint64) {
+	hBaseAfs := int32(stack[0])
+	ptrRegExp := uint32(stack[1])
+	lenRegExp := uint32(stack[2])
+	strRegExp, ok := mod.Memory().Read(ptrRegExp, lenRegExp)
+	if !ok {
+		var ecode int32 = -3
+		stack[0] = uint64(ecode)
+		return
+	}
+	if hBaseAfs <= 0 {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	objBaseAfs, ok := a.api.AferoFsInstancesMgr.Get(hBaseAfs)
+	if !ok {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	cpdRegExp, err := regexp.Compile(string(strRegExp))
+	if err != nil {
+		a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+			"type": "afs_regfs_regexp_compile_error",
+			"msg":  err.Error(),
+		})
+		var ecode int32 = -4
+		stack[0] = uint64(ecode)
+		return
+	}
+	regfs := afero.NewRegexpFs(objBaseAfs.Fs, cpdRegExp)
+	afsObj := &AferoFsInstance{
+		Fs:              regfs,
+		TypeDescription: "afero-regexp-fs",
+	}
+	h, err := a.api.AferoFsInstancesMgr.AllocateAndPut(afsObj)
+	if err != nil {
+		if err == go_int32_handle.ErrHandleExceedMax {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  "handle exceed max",
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		} else {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  err.Error(),
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		}
+	}
+	a.logger.LogComplex("ngvfs-debug", hiedalog.DLN_DEBUG, map[string]string{
+		"type":     "afs_create_regfs_ok",
+		"handle":   fmt.Sprintf("%d", h),
+		"base-afs": fmt.Sprintf("%d", hBaseAfs),
+		"regexp":   string(strRegExp),
+	})
+	stack[0] = uint64(h)
+	return
+}
+
+func (a *InitWAVMHostModuleAdapter) efnAfsCreateRoFs(_ context.Context, _ wa_api.Module, stack []uint64) {
+	hBaseAfs := int32(stack[0])
+	if hBaseAfs <= 0 {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	objBaseAfs, ok := a.api.AferoFsInstancesMgr.Get(hBaseAfs)
+	if !ok {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	rofs := afero.NewReadOnlyFs(objBaseAfs.Fs)
+	afsObj := &AferoFsInstance{
+		Fs:              rofs,
+		TypeDescription: "afero-readonly-fs",
+	}
+	h, err := a.api.AferoFsInstancesMgr.AllocateAndPut(afsObj)
+	if err != nil {
+		if err == go_int32_handle.ErrHandleExceedMax {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  "handle exceed max",
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		} else {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  err.Error(),
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		}
+	}
+	a.logger.LogComplex("ngvfs-debug", hiedalog.DLN_DEBUG, map[string]string{
+		"type":     "afs_create_rofs_ok",
+		"handle":   fmt.Sprintf("%d", h),
+		"base-afs": fmt.Sprintf("%d", hBaseAfs),
+	})
+	stack[0] = uint64(h)
+	return
+}
+func (a *InitWAVMHostModuleAdapter) efnAfsCreateCowFs(_ context.Context, _ wa_api.Module, stack []uint64) {
+	hRoAfs := int32(stack[0])
+	hWrAfs := int32(stack[1])
+	if hRoAfs <= 0 {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	objRoAfs, ok := a.api.AferoFsInstancesMgr.Get(hRoAfs)
+	if !ok {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	if hWrAfs <= 0 {
+		var ecode int32 = -3
+		stack[0] = uint64(ecode)
+		return
+	}
+	objWrAfs, ok := a.api.AferoFsInstancesMgr.Get(hWrAfs)
+	if !ok {
+		var ecode int32 = -3
+		stack[0] = uint64(ecode)
+		return
+	}
+	cowfs := afero.NewCopyOnWriteFs(objRoAfs.Fs, objWrAfs.Fs)
+	afsObj := &AferoFsInstance{
+		Fs:              cowfs,
+		TypeDescription: "afero-copy-on-write-fs",
+	}
+	h, err := a.api.AferoFsInstancesMgr.AllocateAndPut(afsObj)
+	if err != nil {
+		if err == go_int32_handle.ErrHandleExceedMax {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  "handle exceed max",
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		} else {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  err.Error(),
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		}
+	}
+	a.logger.LogComplex("ngvfs-debug", hiedalog.DLN_DEBUG, map[string]string{
+		"type":        "afs_create_cowfs_ok",
+		"handle":      fmt.Sprintf("%d", h),
+		"base-ro-afs": fmt.Sprintf("%d", hRoAfs),
+		"base-wr-afs": fmt.Sprintf("%d", hWrAfs),
+	})
+	stack[0] = uint64(h)
+	return
+}
+func (a *InitWAVMHostModuleAdapter) efnAfsCreateCorFs(_ context.Context, _ wa_api.Module, stack []uint64) {
+	hRoAfs := int32(stack[0])
+	hWrAfs := int32(stack[1])
+	cacheTime := int32(stack[3])
+	if cacheTime < 0 {
+		var ecode int32 = -4
+		stack[0] = uint64(ecode)
+		return
+	}
+	if hRoAfs <= 0 {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	objRoAfs, ok := a.api.AferoFsInstancesMgr.Get(hRoAfs)
+	if !ok {
+		var ecode int32 = -2
+		stack[0] = uint64(ecode)
+		return
+	}
+	if hWrAfs <= 0 {
+		var ecode int32 = -3
+		stack[0] = uint64(ecode)
+		return
+	}
+	objWrAfs, ok := a.api.AferoFsInstancesMgr.Get(hWrAfs)
+	if !ok {
+		var ecode int32 = -3
+		stack[0] = uint64(ecode)
+		return
+	}
+	cacheTimeDuration := time.Duration(cacheTime) * time.Second
+	corfs := afero.NewCacheOnReadFs(objRoAfs.Fs, objWrAfs.Fs, cacheTimeDuration)
+	afsObj := &AferoFsInstance{
+		Fs:              corfs,
+		TypeDescription: "afero-cache-on-read-fs",
+	}
+	h, err := a.api.AferoFsInstancesMgr.AllocateAndPut(afsObj)
+	if err != nil {
+		if err == go_int32_handle.ErrHandleExceedMax {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  "handle exceed max",
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		} else {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  err.Error(),
+			})
+			var ecode int32 = -1
+			stack[0] = uint64(ecode)
+			return
+		}
+	}
+	a.logger.LogComplex("ngvfs-debug", hiedalog.DLN_DEBUG, map[string]string{
+		"type":        "afs_create_corfs_ok",
+		"handle":      fmt.Sprintf("%d", h),
+		"base-ro-afs": fmt.Sprintf("%d", hRoAfs),
+		"base-wr-afs": fmt.Sprintf("%d", hWrAfs),
+		"cache-time":  cacheTimeDuration.String(),
+	})
+	stack[0] = uint64(h)
+	return
+}
+
+func (a *InitWAVMHostModuleAdapter) efnAfsCreateBepOvFs(_ context.Context, mod wa_api.Module, stack []uint64) {
+	ptrAfsArray := uint32(stack[0])
+	lenAfsArray := uint32(stack[1])
+	intIsWritable := int32(stack[2])
+	boolIsWritable := intIsWritable > 0
+	if lenAfsArray == 0 {
+		var ecode int32 = -1
+		stack[0] = uint64(ecode)
+		return
+	}
+	sliceAfsHandleArray := make([]int32, lenAfsArray)
+	var i uint32
+	for i = 0; i < lenAfsArray; i++ {
+		offset := ptrAfsArray + i*4
+		b, ok := mod.Memory().Read(offset, 4)
+		if !ok {
+			var ecode int32 = -2
+			stack[0] = uint64(ecode)
+			return
+		}
+		if len(b) != 4 {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type":     "afs_create_bep_ovfs_error",
+				"sub-type": "read_fs_layer_array_error",
+				"msg":      "the length of the array element is not 4",
+				"i":        fmt.Sprintf("%d", i),
+				"offset":   fmt.Sprintf("%d", offset),
+				"len":      fmt.Sprintf("%d", len(b)),
+			})
+			var ecode int32 = -3
+			stack[0] = uint64(ecode)
+			return
+		}
+		sliceAfsHandleArray[i] = int32(binary.LittleEndian.Uint32(b))
+	}
+	afsObjArray := make([]afero.Fs, lenAfsArray)
+	for i, v := range sliceAfsHandleArray {
+		if v <= 0 {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type":     "afs_create_bep_ovfs_error",
+				"sub-type": "read_fs_layer_invalid_handle",
+				"msg":      "the value of this element is a invalid handle",
+				"i":        fmt.Sprintf("%d", i),
+				"handle":   fmt.Sprintf("%d", v),
+			})
+			var ecode int32 = -4
+			stack[0] = uint64(ecode)
+			return
+		}
+		objV, ok := a.api.AferoFsInstancesMgr.Get(v)
+		if !ok {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type":     "afs_create_bep_ovfs_error",
+				"sub-type": "read_fs_layer_invalid_handle",
+				"msg":      "the value of this element is a invalid handle",
+				"i":        fmt.Sprintf("%d", i),
+				"handle":   fmt.Sprintf("%d", v),
+			})
+			var ecode int32 = -4
+			stack[0] = uint64(ecode)
+			return
+		}
+		a.logger.LogComplex("ngvfs", hiedalog.DLN_DEBUG, map[string]string{
+			"type":     "afs_create_bep_ovfs_debug",
+			"sub-type": "get_valid_fs_layer",
+			"i":        fmt.Sprintf("%d", i),
+			"handle":   fmt.Sprintf("%d", v),
+			"fs-name":  objV.Fs.Name(),
+			"fs-desc":  objV.TypeDescription,
+		})
+		afsObjArray[i] = objV.Fs
+	}
+	ovfsOpt := overlayfs.Options{
+		Fss:           afsObjArray,
+		FirstWritable: boolIsWritable,
+		DirsMerger:    nil,
+	}
+	ovfs := overlayfs.New(ovfsOpt)
+	afsObj := &AferoFsInstance{
+		Fs:              ovfs,
+		TypeDescription: "3rd-party-bep-overlayfs",
+	}
+	h, err := a.api.AferoFsInstancesMgr.AllocateAndPut(afsObj)
+	if err != nil {
+		if err == go_int32_handle.ErrHandleExceedMax {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  "handle exceed max",
+			})
+			var ecode int32 = -5
+			stack[0] = uint64(ecode)
+			return
+		} else {
+			a.logger.LogComplex("ngvfs", hiedalog.DLN_ERROR, map[string]string{
+				"type": "afs_handle_alloc_error",
+				"msg":  err.Error(),
+			})
+			var ecode int32 = -5
+			stack[0] = uint64(ecode)
+			return
+		}
+	}
+	a.logger.LogComplex("ngvfs-debug", hiedalog.DLN_DEBUG, map[string]string{
+		"type":   "afs_create_bep_ovfs_ok",
+		"handle": fmt.Sprintf("%d", h),
+	})
+	stack[0] = uint64(h)
+	return
+}

+ 216 - 0
openngvfs/wa_init_vm.go

@@ -0,0 +1,216 @@
+package openngvfs
+
+import (
+	"context"
+	"fmt"
+	"git.swzry.com/zry/GoHiedaLogger/hiedalog"
+	"git.swzry.com/zry/pathutils"
+	"github.com/tetratelabs/wazero"
+	wa_api "github.com/tetratelabs/wazero/api"
+	"github.com/tetratelabs/wazero/imports/wasi_snapshot_preview1"
+	"os"
+	"path"
+)
+
+type InitWAVM struct {
+	logger          *hiedalog.HiedaLogger
+	ctx             context.Context
+	runtime         wazero.Runtime
+	mainModCompiled wazero.CompiledModule
+	mainModCfg      wazero.ModuleConfig
+	mainMod         wa_api.Module
+	hostMod         wa_api.Module
+	pStdout         *IOWriterAdapter
+	pStderr         *IOWriterAdapter
+	hmAdapter       *InitWAVMHostModuleAdapter
+	fsTab           *RawFsTab
+	appName         string
+}
+
+func NewInitWAVM(ctx context.Context, api *InitWAVMHostAPI, ngRoot string, appName string, logger *hiedalog.HiedaLogger) (*InitWAVM, error) {
+	ok, err := pathutils.PathExists(ngRoot)
+	if err != nil {
+		return nil, fmt.Errorf("failed check if NAGAE_ROV_ROOT is exists: %w", err)
+	}
+	if !ok {
+		return nil, fmt.Errorf("NAGAE_ROV_ROOT '%s' not exists", ngRoot)
+	}
+	pcache := path.Join(ngRoot, "cache", "wasm", "ngvfs", "app", appName)
+	err = pathutils.MkDirIfNotExist(pcache, 0755)
+	if !ok {
+		return nil, fmt.Errorf("failed create dir '%s' for wasm cache: %w", pcache, err)
+	}
+	pwar := path.Join(ngRoot, "misc", "wasm", "common", "osfs")
+	err = pathutils.MkDirIfNotExist(pwar, 0755)
+	if !ok {
+		return nil, fmt.Errorf("failed create dir '%s' for wasm common osfs: %w", pcache, err)
+	}
+	fscfgRoot := path.Join(ngRoot, "syscfg", "ngvfs")
+	fstabPath := path.Join(fscfgRoot, "fstab.toml")
+	waRoot := path.Join(fscfgRoot, "wasm")
+	fsTab, err := loadFsTab(fstabPath)
+	if err != nil {
+		return nil, fmt.Errorf("failed load fstab: %w", err)
+	}
+	wasmBin, err := getWasmFromFstab(waRoot, fsTab, appName, logger)
+	if err != nil {
+		return nil, fmt.Errorf("failed load init wasm: %w", err)
+	}
+	cc, err := wazero.NewCompilationCacheWithDir(pcache)
+	if !ok {
+		return nil, fmt.Errorf("failed create dir '%s' for wasm cache: %w", pcache, err)
+	}
+	rtcfg := wazero.NewRuntimeConfig().WithCompilationCache(cc)
+	rt := wazero.NewRuntimeWithConfig(ctx, rtcfg)
+	_, err = wasi_snapshot_preview1.Instantiate(ctx, rt)
+	if !ok {
+		return nil, fmt.Errorf("failed instantiate wasi: %w", err)
+	}
+	wafscfg := wazero.NewFSConfig().
+		WithReadOnlyDirMount(pwar, "/osfs")
+	pStdout := NewIOWriterAdapter(logger, "ngvfs-vm-stdio", hiedalog.DLN_INFO)
+	pStderr := NewIOWriterAdapter(logger, "ngvfs-vm-stdio", hiedalog.DLN_WARN)
+	mcfg := wazero.NewModuleConfig().
+		WithFSConfig(wafscfg).
+		WithStderr(pStderr).
+		WithStdout(pStdout).
+		WithEnv("NAGAE_ROV_ROOT", ngRoot).
+		WithEnv("NAGAE_APP_NAME", appName)
+	mainModCompiled, err := rt.CompileModule(ctx, wasmBin)
+	hostModuleBuilder := rt.NewHostModuleBuilder("ngvfs_init")
+	apiAdapter := NewInitWAVMHostModuleAdapter(hostModuleBuilder, api, fsTab, logger, appName)
+	if err != nil {
+		return nil, fmt.Errorf("failed compile wasm code: %w", err)
+	}
+	vm := &InitWAVM{
+		ctx:             ctx,
+		runtime:         rt,
+		mainModCompiled: mainModCompiled,
+		mainModCfg:      mcfg,
+		logger:          logger,
+		pStderr:         pStderr,
+		pStdout:         pStdout,
+		hmAdapter:       apiAdapter,
+		fsTab:           fsTab,
+		appName:         appName,
+	}
+	return vm, nil
+}
+
+func (vm *InitWAVM) Init() error {
+	if vm.pStderr == nil {
+		return fmt.Errorf("internal error: pStderr is nil")
+	}
+	if vm.pStdout == nil {
+		return fmt.Errorf("internal error: pStdout is nil")
+	}
+	go func() {
+		vm.pStdout.Run()
+		vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_INFO, map[string]string{
+			"type": "stdio_routine_end",
+			"msg":  "stdout routine end",
+		})
+	}()
+	go func() {
+		vm.pStderr.Run()
+		vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_INFO, map[string]string{
+			"type": "stdio_routine_end",
+			"msg":  "stderr routine end",
+		})
+	}()
+	hm, err := vm.hmAdapter.Instantiate(vm.ctx)
+	if err != nil {
+		return fmt.Errorf("failed instantiate host module: %w", err)
+	}
+	vm.hostMod = hm
+	mainMod, err := vm.runtime.InstantiateModule(vm.ctx, vm.mainModCompiled, vm.mainModCfg)
+	if err != nil {
+		return fmt.Errorf("failed instantiate WASM code: %w", err)
+	}
+	vm.mainMod = mainMod
+	return nil
+}
+
+func (vm *InitWAVM) AutoMount() error {
+	if vm.mainMod == nil {
+		return fmt.Errorf("call AutoMount before initialized")
+	}
+	amfn := vm.mainMod.ExportedFunction("auto_mount")
+	if amfn == nil {
+		return fmt.Errorf("exported function 'auto_mount' not found")
+	}
+	ret, err := amfn.Call(vm.ctx)
+	if err != nil {
+		return fmt.Errorf("failed call 'auto_mount': %w", err)
+	}
+	if len(ret) != 1 {
+		return fmt.Errorf("unexpected number of return values: %d", len(ret))
+	}
+	retval := ret[0]
+	if retval != 0 {
+		vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_ERROR, map[string]string{
+			"type": "auto_mount_fail",
+			"msg":  fmt.Sprintf("auto mount returned %d", ret[0]),
+		})
+		return fmt.Errorf("error when calling `auto_mount`: returned non-zero vaule: %d", retval)
+	}
+	vm.logger.LogComplex("ngvfs-vm", hiedalog.DLN_INFO, map[string]string{
+		"type": "auto_mount_ok",
+		"msg":  fmt.Sprintf("auto mount ok"),
+	})
+	return nil
+}
+
+func (vm *InitWAVM) DeInit() error {
+	if vm.pStdout == nil {
+		vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+			"type": "deinit_warn",
+			"msg":  "stdout is not initialized",
+		})
+	} else {
+		err := vm.pStdout.Close()
+		if err != nil {
+			vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "deinit_warn",
+				"msg":  "close stdout error",
+				"err":  err.Error(),
+			})
+		}
+	}
+	if vm.pStderr == nil {
+		vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+			"type": "deinit_warn",
+			"msg":  "stderr is not initialized",
+		})
+	} else {
+		err := vm.pStderr.Close()
+		if err != nil {
+			vm.logger.LogComplex("ngvfs", hiedalog.DLN_WARN, map[string]string{
+				"type": "deinit_warn",
+				"msg":  "close stderr error",
+				"err":  err.Error(),
+			})
+		}
+	}
+	return nil
+}
+
+func getWasmFromFstab(waRoot string, fstab *RawFsTab, appName string, logger *hiedalog.HiedaLogger) (wasmBin []byte, err error) {
+	var ok bool
+	var waName string
+	waName, ok = fstab.Wasm.App[appName]
+	if !ok {
+		waName = fstab.Wasm.Default
+	}
+	waFileName := path.Join(waRoot, waName+".wasm")
+	logger.LogComplex("ngvfs", hiedalog.DLN_INFO, map[string]string{
+		"type":      "will_init_by_wasm",
+		"wasm_name": waName,
+		"wasm_file": waFileName,
+	})
+	wasmBin, err = os.ReadFile(waFileName)
+	if err != nil {
+		return nil, fmt.Errorf("failed read wasm file: %w", err)
+	}
+	return wasmBin, nil
+}

+ 41 - 0
testing/standalone-test-0001/go/go.mod

@@ -0,0 +1,41 @@
+module git.swzry.com/zry/openNagaeFS/pkgtest/vfs_v0_1_0_test_0001/go
+
+go 1.21.6
+
+require (
+	git.swzry.com/zry/GoHiedaLogger/hieda_ginutil v0.0.0-20230814164330-c2545e8bfba1
+	git.swzry.com/zry/GoHiedaLogger/hiedabke_console v0.0.0-20230814164330-c2545e8bfba1
+	git.swzry.com/zry/GoHiedaLogger/hiedalog v0.0.0-20230814164330-c2545e8bfba1
+	git.swzry.com/zry/afero2webdav/afero2webdav v0.0.0-20240317163434-9dd5dbd71379
+	git.swzry.com/zry/pathutils v0.0.0-20221111111354-c1a495112ba9
+	github.com/gin-gonic/gin v1.9.1
+	golang.org/x/net v0.19.0
+)
+
+require (
+	github.com/bytedance/sonic v1.9.1 // indirect
+	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+	github.com/gin-contrib/sse v0.1.0 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-playground/validator/v10 v10.14.0 // indirect
+	github.com/goccy/go-json v0.10.2 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
+	github.com/leodido/go-urn v1.2.4 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
+	github.com/spf13/afero v1.11.0 // indirect
+	github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/ugorji/go/codec v1.2.11 // indirect
+	golang.org/x/arch v0.3.0 // indirect
+	golang.org/x/crypto v0.16.0 // indirect
+	golang.org/x/sys v0.15.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/protobuf v1.31.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)

+ 101 - 0
testing/standalone-test-0001/go/go.sum

@@ -0,0 +1,101 @@
+git.swzry.com/zry/GoHiedaLogger/hieda_ginutil v0.0.0-20230814164330-c2545e8bfba1 h1:gQct/rrBbwIOooVQ+JKtSPO+9uQy8jO9oKd4PrqZ70g=
+git.swzry.com/zry/GoHiedaLogger/hieda_ginutil v0.0.0-20230814164330-c2545e8bfba1/go.mod h1:P+s0sqwU9FFLYxE6fbDRL8wbmbennR8pBkdQh0/b4j4=
+git.swzry.com/zry/GoHiedaLogger/hiedabke_console v0.0.0-20230814164330-c2545e8bfba1 h1:68DRimhaFe4Tvx4kMIDNts/VpOnt4LxkRVXGmpOeHmc=
+git.swzry.com/zry/GoHiedaLogger/hiedabke_console v0.0.0-20230814164330-c2545e8bfba1/go.mod h1:QKGs1+W8+y6/RMTSaAv6LwJnY9/7zaGs38IbwmYlFbo=
+git.swzry.com/zry/GoHiedaLogger/hiedalog v0.0.0-20230814164330-c2545e8bfba1 h1:5lcgeoBT7tvU/XH9jKlzQguiYjLbdh7SY4UsUf/Ek00=
+git.swzry.com/zry/GoHiedaLogger/hiedalog v0.0.0-20230814164330-c2545e8bfba1/go.mod h1:NMU7558kNXCUuK0qKYQMtYK/kn2lhwelnij295H3pdU=
+git.swzry.com/zry/afero2webdav/afero2webdav v0.0.0-20240317163434-9dd5dbd71379 h1:NtkyS0vJz8y5/n2NgW/Aymz6EtTcLiioL9WywwOOf+c=
+git.swzry.com/zry/afero2webdav/afero2webdav v0.0.0-20240317163434-9dd5dbd71379/go.mod h1:pliYWNqjo3BBkpmV1C4Vq6bPpn/OJsWmWBFR157fg6Y=
+git.swzry.com/zry/pathutils v0.0.0-20221111111354-c1a495112ba9 h1:QgQYkCcmA3hJa2SD54i5VUruKf7IO4pzhPiSaml/zjI=
+git.swzry.com/zry/pathutils v0.0.0-20221111111354-c1a495112ba9/go.mod h1:BhIO8/5pXEldM0+n3TvdRguR4cfD7Th6YwdTdu7p508=
+github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
+github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
+github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31 h1:OXcKh35JaYsGMRzpvFkLv/MEyPuL49CThT1pZ8aSml4=
+github.com/ttacon/chalk v0.0.0-20160626202418-22c06c80ed31/go.mod h1:onvgF043R+lC5RZ8IT9rBXDaEDnpnw/Cl+HFiw+v/7Q=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
+golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
+google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

+ 81 - 0
testing/standalone-test-0001/go/main.go

@@ -0,0 +1,81 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"git.swzry.com/zry/GoHiedaLogger/hieda_ginutil"
+	"git.swzry.com/zry/GoHiedaLogger/hiedabke_console"
+	"git.swzry.com/zry/GoHiedaLogger/hiedalog"
+	"git.swzry.com/zry/afero2webdav/afero2webdav"
+	ngvfs "git.swzry.com/zry/openNGVFS/openngvfs"
+	"git.swzry.com/zry/pathutils"
+	"github.com/gin-gonic/gin"
+	"golang.org/x/net/webdav"
+	"net/http"
+	"os"
+	"strings"
+	"time"
+)
+
+func main() {
+	root, err := pathutils.GetCurrentDirectory()
+	if err != nil {
+		panic(fmt.Errorf("failed to get executable directory: %v", err))
+	}
+	logger := hiedalog.NewHiedaLogger()
+	consoleBke := hiedabke_console.NewConsoleBackend(os.Stderr)
+	logger.AddBackend(consoleBke, logger.LevelFilter.NameToID(hiedalog.DLN_DEBUG))
+	fs := ngvfs.NewOpenNagaeVFS(logger)
+	ctx := context.Background()
+	defer func() {
+		err = fs.DeInit()
+		if err != nil {
+			fmt.Println("error deinitializing fs: ", err)
+		}
+	}()
+	err = fs.Init(ctx, root, "test")
+	if err != nil {
+		fmt.Println("error initializing fs: ", err)
+		return
+	}
+	err = fs.RunMount()
+	if err != nil {
+		fmt.Println("error in run auto-mount: ", err)
+		return
+	}
+	wfs := afero2webdav.NewAfero2Webdav(fs)
+	hdl := &webdav.Handler{
+		Prefix:     "/",
+		FileSystem: wfs,
+		LockSystem: webdav.NewMemLS(),
+		Logger: func(request *http.Request, err error) {
+			if err != nil {
+				logger.LogComplex("webdav", hiedalog.DLN_WARN, map[string]string{
+					"type": "webdav-error",
+					"msg":  err.Error(),
+				})
+			}
+		},
+	}
+	websvr := gin.Default()
+	websvr.Use(hieda_ginutil.GinLoggerWithComplexLogger(hieda_ginutil.GinLoggerConfig{
+		Logger:       logger,
+		ModuleName:   "web",
+		LevelMapFunc: hieda_ginutil.GetDefaultLevelMapFunc(),
+	}))
+	websvr.Use(func(c *gin.Context) {
+		if strings.HasPrefix(c.Request.URL.Path, hdl.Prefix) {
+			c.Status(200)
+			hdl.ServeHTTP(c.Writer, c.Request)
+			c.Abort()
+		}
+	})
+	err = websvr.Run(":5821")
+	if err != nil {
+		fmt.Println("error in http server: ", err)
+		return
+	}
+	logger.LogString("main", hiedalog.DLN_INFO, "delay 100ms...")
+	time.Sleep(time.Millisecond * 100)
+	logger.LogString("main", hiedalog.DLN_INFO, "Done.")
+}

+ 37 - 0
testing/standalone-test-0001/justfile

@@ -0,0 +1,37 @@
+jfdir := replace(justfile_directory(), "\\", "/")
+dist := jfdir / "dist"
+misc := jfdir / "misc"
+
+dist_cfg_dir := dist / "syscfg" / "ngvfs"
+
+host_src := jfdir / "go"
+wasm_src_root := jfdir / "openngvfs_init_wasm_test"
+
+wasm_dist_s_debug := "target" / "wasm32-wasi" / "debug"
+wasm_dist_to_dir := dist_cfg_dir / "wasm"
+
+default:
+	just --list
+
+env:
+	mkdir -p {{ wasm_dist_to_dir }}
+	cp {{ misc / "fstab.toml" }} {{ dist_cfg_dir / "fstab.toml" }}
+
+host:
+	cd {{host_src}}; go build -o {{ dist / "test.exe" }}
+
+host_tidy:
+	cd {{host_src}}; go mod tidy -e
+
+wasm:
+	cd {{ wasm_src_root }}; cargo build --target="wasm32-wasi"
+	cp {{ wasm_src_root / wasm_dist_s_debug / "openngvfs_init_wasm_test.wasm" }} {{ wasm_dist_to_dir / "default.wasm"}}
+
+run:
+	cd {{ dist }}; ./test.exe
+
+all:
+	just env
+	just host
+	just wasm
+	just run

+ 11 - 0
testing/standalone-test-0001/misc/fstab.toml

@@ -0,0 +1,11 @@
+[wasm]
+default = "default"
+app = {}
+
+[exkv.default]
+foo = "hello"
+bar = "world"
+
+[exkv.app.test]
+bar = "gensoukyo"
+foobar = "nyan"

+ 10 - 0
testing/standalone-test-0001/openngvfs_init_wasm_test/Cargo.toml

@@ -0,0 +1,10 @@
+[package]
+name = "openngvfs_init_wasm_test"
+version = "1.0.0"
+edition = "2021"
+publish = false
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+openngvfs_init_wasm_lib = { path = "../../../init-wasm/openngvfs_init_wasm_lib" }

+ 81 - 0
testing/standalone-test-0001/openngvfs_init_wasm_test/src/main.rs

@@ -0,0 +1,81 @@
+use openngvfs_init_wasm_lib::{api_ver_get, set_auto_mount_handler, exkv_has_key, exkv_get, get_app_name, get_nagae_rov_root, vfs_mkdir};
+use openngvfs_init_wasm_lib::afs::AfsInstance;
+use openngvfs_init_wasm_lib::bep_ovfs::AfsBepOvFsInstance;
+use openngvfs_init_wasm_lib::error::{GetKeyError};
+
+fn main() {
+    println!("Hello, world!");
+    set_auto_mount_handler(auto_mount_handler);
+    let (api_maj, api_min, api_pat) = api_ver_get();
+    println!("API Version: {}.{}.{}", api_maj, api_min, api_pat);
+    println!("App Name: {}", get_app_name())
+}
+
+fn auto_mount_handler() -> i32 {
+    println!("auto mount test");
+    println!("auto mount test done.");
+    println!("==== Test for exkv_has_key ====");
+    has_key_test("test");
+    has_key_test("foo");
+    has_key_test("bar");
+    has_key_test("foobar");
+    println!("========");
+    println!("==== Test for exkv_get ====");
+    get_test("test");
+    get_test("foo");
+    get_test("bar");
+    get_test("foobar");
+    println!("========");
+    let ng_root = get_nagae_rov_root();
+    println!("nagae root: {}", &ng_root);
+    println!("==== Mount Test ====");
+    let root = AfsInstance::new_afero_memfs();
+    root.mount("/").unwrap();
+    vfs_mkdir("/local", 0666).unwrap();
+    vfs_mkdir("/ro", 0666).unwrap();
+    //vfs_mkdir("/wasm", 0666).unwrap();
+    let osfs = AfsInstance::new_afero_osfs();
+    let local = AfsInstance::new_afero_bpfs(&osfs, &ng_root);
+    local.mount("/local").unwrap();
+    let local_ro = AfsInstance::new_afero_rofs(&local);
+    local_ro.mount("/ro").unwrap();
+    // afero.RegexpFs has some bugs with Readdir.
+    //let only_wasm = AfsInstance::new_afero_regfs(&local_ro, "\\.wasm$");
+    //only_wasm.mount("/wasm").unwrap();
+    let rwcache_path = format!("{}/{}", ng_root, "cache/rwtest");
+    let rwcache = AfsInstance::new_afero_bpfs(&osfs, &rwcache_path);
+    root.mkdir("/overlay", 0666).unwrap();
+    let overlay_fslayer = vec![&rwcache, &local_ro];
+    let overlay = AfsBepOvFsInstance::new(&overlay_fslayer, true).unwrap();
+    overlay.mount("/overlay").unwrap();
+    0
+}
+
+fn has_key_test(key: &str) {
+    let ret = exkv_has_key(key);
+    if ret {
+        println!("key exists: {}", key);
+    }else{
+        println!("key not exists: {}", key);
+    }
+}
+
+fn get_test(key: &str) {
+    let ret = exkv_get(key);
+    match ret {
+        Ok(v) => {
+            let s = String::from_utf8_lossy(v.as_slice());
+            println!("value of key {}: {}", key, s);
+        }
+        Err(e) => {
+            match e {
+                GetKeyError::KeyNotExists => {
+                    println!("failed get value of key {}: key not exists", key);
+                }
+                GetKeyError::InternalError(emsg) => {
+                    println!("failed get value of key {}: internal error: {:?}", key, emsg);
+                }
+            }
+        }
+    };
+}