summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2024-07-19 01:48:28 +0700
committerShulhan <ms@kilabit.info>2024-07-22 00:38:19 +0700
commit52ef9967fca5946ee047847d744d00ccaecff1a8 (patch)
treeeef098b02e56308e228002e3c6016bbd81f4870b
parent1093c8ce4ae0015e4d3d1f654c0e2e5d2ddd28a7 (diff)
downloadpakakeh.go-52ef9967fca5946ee047847d744d00ccaecff1a8.tar.xz
lib/memfs: sanitize the Root directory to fix refresh
In [MemFS.refresh], if the requested url is "/file1" and [Options.Root] is ".", the path during refresh become "file1" and if passed to [filepath.Dir] it will return ".". This cause the loop on refresh never end because there is no PathNodes equal with ".".
-rw-r--r--lib/memfs/internal/testdata/get_refresh_test.data90
-rw-r--r--lib/memfs/memfs.go12
-rw-r--r--lib/memfs/memfs_test.go164
-rw-r--r--lib/memfs/options.go6
4 files changed, 249 insertions, 23 deletions
diff --git a/lib/memfs/internal/testdata/get_refresh_test.data b/lib/memfs/internal/testdata/get_refresh_test.data
index 1cf9b8cc..ce3e5d9e 100644
--- a/lib/memfs/internal/testdata/get_refresh_test.data
+++ b/lib/memfs/internal/testdata/get_refresh_test.data
@@ -2,10 +2,46 @@ Test Get that trigger refresh.
The MemFS Options TryDirect must be set to true.
If the input content is not set then the file will not be written.
->>> /dir-a/dir-b/file
-Content of file.
+<<< /
+{
+ "path": "/",
+ "name": "/",
+ "content_type": "",
+ "mode_string": "drwxr-xr-x",
+ "size": 0,
+ "is_dir": true,
+ "childs": []
+}
+
+>>> /file1
+Content of file1.
+
+<<< /file1
+{
+ "path": "/",
+ "name": "/",
+ "content_type": "",
+ "mode_string": "drwxr-xr-x",
+ "size": 0,
+ "is_dir": true,
+ "childs": [
+ {
+ "path": "/file1",
+ "name": "file1",
+ "content_type": "text/plain; charset=utf-8",
+ "mode_string": "-rw-------",
+ "size": 17,
+ "is_dir": false,
+ "content": "Q29udGVudCBvZiBmaWxlMS4=",
+ "childs": []
+ }
+ ]
+}
+
+>>> /dir-a/dir-b/file2
+Content of file2.
-<<< /dir-a/dir-b/file
+<<< /dir-a/dir-b/file2
{
"path": "/",
"name": "/",
@@ -15,6 +51,16 @@ Content of file.
"is_dir": true,
"childs": [
{
+ "path": "/file1",
+ "name": "file1",
+ "content_type": "text/plain; charset=utf-8",
+ "mode_string": "-rw-------",
+ "size": 17,
+ "is_dir": false,
+ "content": "Q29udGVudCBvZiBmaWxlMS4=",
+ "childs": []
+ },
+ {
"path": "/dir-a",
"name": "dir-a",
"content_type": "",
@@ -31,13 +77,13 @@ Content of file.
"is_dir": true,
"childs": [
{
- "path": "/dir-a/dir-b/file",
- "name": "file",
+ "path": "/dir-a/dir-b/file2",
+ "name": "file2",
"content_type": "text/plain; charset=utf-8",
"mode_string": "-rw-------",
- "size": 16,
+ "size": 17,
"is_dir": false,
- "content": "Q29udGVudCBvZiBmaWxlLg==",
+ "content": "Q29udGVudCBvZiBmaWxlMi4=",
"childs": []
}
]
@@ -47,10 +93,10 @@ Content of file.
]
}
->>> /dir-a/dir-b/file2
-Content of file2.
+>>> /dir-a/dir-b/file3
+Content of file3.
-<<< /dir-a/dir-b/file2
+<<< /dir-a/dir-b/file3
{
"path": "/",
"name": "/",
@@ -60,6 +106,16 @@ Content of file2.
"is_dir": true,
"childs": [
{
+ "path": "/file1",
+ "name": "file1",
+ "content_type": "text/plain; charset=utf-8",
+ "mode_string": "-rw-------",
+ "size": 17,
+ "is_dir": false,
+ "content": "Q29udGVudCBvZiBmaWxlMS4=",
+ "childs": []
+ },
+ {
"path": "/dir-a",
"name": "dir-a",
"content_type": "",
@@ -76,23 +132,23 @@ Content of file2.
"is_dir": true,
"childs": [
{
- "path": "/dir-a/dir-b/file",
- "name": "file",
+ "path": "/dir-a/dir-b/file2",
+ "name": "file2",
"content_type": "text/plain; charset=utf-8",
"mode_string": "-rw-------",
- "size": 16,
+ "size": 17,
"is_dir": false,
- "content": "Q29udGVudCBvZiBmaWxlLg==",
+ "content": "Q29udGVudCBvZiBmaWxlMi4=",
"childs": []
},
{
- "path": "/dir-a/dir-b/file2",
- "name": "file2",
+ "path": "/dir-a/dir-b/file3",
+ "name": "file3",
"content_type": "text/plain; charset=utf-8",
"mode_string": "-rw-------",
"size": 17,
"is_dir": false,
- "content": "Q29udGVudCBvZiBmaWxlMi4=",
+ "content": "Q29udGVudCBvZiBmaWxlMy4=",
"childs": []
}
]
diff --git a/lib/memfs/memfs.go b/lib/memfs/memfs.go
index 6ef40c2d..4ac9eba0 100644
--- a/lib/memfs/memfs.go
+++ b/lib/memfs/memfs.go
@@ -637,6 +637,13 @@ out:
func (mfs *MemFS) refresh(url string) (node *Node, err error) {
syspath := filepath.Join(mfs.Root.SysPath, url)
+ if syspath[0] != '/' {
+ // When "." joined with url "/file", the syspath become
+ // "file" instead of "./file", this cause
+ // [strings.HasPrefix] return false.
+ syspath = `./` + syspath
+ }
+
if !strings.HasPrefix(syspath, mfs.Root.SysPath) {
return nil, fs.ErrNotExist
}
@@ -655,6 +662,11 @@ func (mfs *MemFS) refresh(url string) (node *Node, err error) {
for node == nil {
dir = filepath.Dir(dir)
+ if dir == `.` {
+ // On "./file" it will return ".".
+ node = mfs.Root
+ break
+ }
node = mfs.PathNodes.Get(dir)
}
diff --git a/lib/memfs/memfs_test.go b/lib/memfs/memfs_test.go
index 12651413..8275ea3b 100644
--- a/lib/memfs/memfs_test.go
+++ b/lib/memfs/memfs_test.go
@@ -65,11 +65,46 @@ func TestNew(t *testing.T) {
opts Options
}
+ var dirTestdata = filepath.Join(_testWD, `testdata`)
+ var err error
+
+ err = os.Chdir(dirTestdata)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Cleanup(func() {
+ err = os.Chdir(_testWD)
+ if err != nil {
+ t.Fatal(err)
+ }
+ })
+
var afile = filepath.Join(_testWD, `testdata/index.html`)
var listCase = []testCase{{
- desc: "With empty dir",
- expErr: "open : no such file or directory",
+ desc: `With empty dir`,
+ expMapKeys: []string{
+ `/`,
+ `/direct`,
+ `/direct/add`,
+ `/direct/add/file`,
+ `/direct/add/file2`,
+ `/exclude`,
+ `/exclude/dir`,
+ `/exclude/index-link.css`,
+ `/exclude/index-link.html`,
+ `/exclude/index-link.js`,
+ `/include`,
+ `/include/dir`,
+ `/include/index.css`,
+ `/include/index.html`,
+ `/include/index.js`,
+ `/index.css`,
+ `/index.html`,
+ `/index.js`,
+ `/plain`,
+ },
}, {
desc: "With file",
opts: Options{
@@ -79,7 +114,7 @@ func TestNew(t *testing.T) {
}, {
desc: "With directory",
opts: Options{
- Root: filepath.Join(_testWD, "testdata"),
+ Root: dirTestdata,
Excludes: []string{
"memfs_generate.go$",
"direct$",
@@ -156,7 +191,6 @@ func TestNew(t *testing.T) {
var (
c testCase
mfs *MemFS
- err error
)
for _, c = range listCase {
t.Log(c.desc)
@@ -392,7 +426,7 @@ func TestMemFS_Get_refresh(t *testing.T) {
var (
tempDir = t.TempDir()
opts = Options{
- Root: tempDir,
+ Root: tempDir + `/`,
TryDirect: true,
}
@@ -405,9 +439,127 @@ func TestMemFS_Get_refresh(t *testing.T) {
}
var listCase = []testCase{{
- filePath: `/dir-a/dir-b/file`,
+ filePath: `/file1`,
}, {
filePath: `/dir-a/dir-b/file2`,
+ }, {
+ filePath: `/dir-a/dir-b/file3`,
+ }}
+
+ var (
+ c testCase
+ gotJSON bytes.Buffer
+ )
+ for _, c = range listCase {
+ var fullpath = filepath.Join(tempDir, c.filePath)
+
+ err = os.MkdirAll(filepath.Dir(fullpath), 0700)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var expContent = tdata.Input[c.filePath]
+ if len(expContent) != 0 {
+ // Only create the file if content is set.
+ err = os.WriteFile(fullpath, expContent, 0600)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+
+ // Try Get the file.
+
+ var (
+ tag = c.filePath + `:error`
+ expError = string(tdata.Output[tag])
+ node *Node
+ )
+
+ node, err = mfs.Get(c.filePath)
+ if err != nil {
+ test.Assert(t, tag, expError, err.Error())
+ continue
+ }
+
+ // Check the tree of MemFS.
+
+ var rawJSON []byte
+
+ rawJSON, err = mfs.Root.JSON(9999, true, false)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ gotJSON.Reset()
+ err = json.Indent(&gotJSON, rawJSON, ``, ` `)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ test.Assert(t, c.filePath+` content`, string(expContent), string(node.Content))
+
+ var expJSON = string(tdata.Output[c.filePath])
+ test.Assert(t, c.filePath+` JSON of memfs.Root`, expJSON, gotJSON.String())
+ }
+}
+
+// TestMemFS_Get_refresh_withDot test [MemFS.refresh] using "." as Root
+// directory.
+func TestMemFS_Get_refresh_withDot(t *testing.T) {
+ type testCase struct {
+ filePath string
+ }
+
+ var (
+ tdata *test.Data
+ err error
+ )
+
+ tdata, err = test.LoadData(`internal/testdata/get_refresh_test.data`)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var (
+ tempDir = t.TempDir()
+ opts = Options{
+ Root: `.`,
+ TryDirect: true,
+ }
+ workDir string
+ mfs *MemFS
+ )
+
+ workDir, err = os.Getwd()
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ err = os.Chdir(tempDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ t.Cleanup(func() {
+ err = os.Chdir(workDir)
+ if err != nil {
+ t.Logf(err.Error())
+ }
+ })
+
+ mfs, err = New(&opts)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ var listCase = []testCase{{
+ filePath: `/`,
+ }, {
+ filePath: `/file1`,
+ }, {
+ filePath: `/dir-a/dir-b/file2`,
+ }, {
+ filePath: `/dir-a/dir-b/file3`,
}}
var (
diff --git a/lib/memfs/options.go b/lib/memfs/options.go
index cae701b2..0b415f5c 100644
--- a/lib/memfs/options.go
+++ b/lib/memfs/options.go
@@ -4,6 +4,8 @@
package memfs
+import "strings"
+
const (
defaultMaxFileSize = 1024 * 1024 * 5
)
@@ -48,4 +50,8 @@ func (opts *Options) init() {
if opts.MaxFileSize == 0 {
opts.MaxFileSize = defaultMaxFileSize
}
+ opts.Root = strings.TrimSuffix(opts.Root, `/`)
+ if len(opts.Root) == 0 {
+ opts.Root = `.`
+ }
}