Skip to content

Commit

Permalink
improve validation of the data path
Browse files Browse the repository at this point in the history
  • Loading branch information
rezkam committed Sep 12, 2023
1 parent 1402dab commit b2dfb45
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 24 deletions.
10 changes: 4 additions & 6 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,13 @@ type OptionSetter func(*Engine) error
// path is where the data files will be stored if the path doesn't exist it will be created
// the user should have write access to the path otherwise an error will be returned
func NewEngine(path string, options ...OptionSetter) (*Engine, error) {

// create the data file directory if it doesn't exist
if err := os.MkdirAll(path, 0755); err != nil {
if err := validateDataPath(path); err != nil {
return nil, err
}

err := validateDataDirAccess(path)
if err != nil {
if exists, err := dataFileExists(path); err != nil {
return nil, err
} else if exists {
return nil, fmt.Errorf("data file exists we have to index it")
}

lockFile, err := createFlock(path)
Expand Down
100 changes: 100 additions & 0 deletions path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package storage

import (
"fmt"
"os"
"path/filepath"
)

const (
dataFileFormatSuffix = ".dat"
)

func validatePathFormat(path string) error {
if path == "" || path[len(path)-1] != '/' {
return fmt.Errorf("path is mandatory and should end with a /")
}
return nil
}

func ensureDataDirectoryExists(path string) error {
stat, err := os.Stat(path)
if err != nil {
if os.IsNotExist(err) {
if err := os.MkdirAll(path, 0o755); err != nil {
return err
} else {
return nil
}
} else {
return err
}
}
if !stat.IsDir() {
return fmt.Errorf("path is not a directory")
}
return nil
}

func validateWriteAccess(path string) error {
testPath := filepath.Join(path, "test-access-file")
testFile, err := os.OpenFile(testPath, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}

_, err = testFile.WriteString("test")
if err != nil {
return err
}

err = testFile.Close()
if err != nil {
return err
}

err = os.Remove(testPath)
if err != nil {
return err
}

return nil
}

func validateDataPath(path string) error {
if err := validatePathFormat(path); err != nil {
return err
}

if err := ensureDataDirectoryExists(path); err != nil {
return err
}

if err := validateWriteAccess(path); err != nil {
return err
}

return nil
}

func dataFileExists(path string) (bool, error) {
exists := false
err := filepath.WalkDir(path, func(path string, d os.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
if info, err := d.Info(); err != nil {
return err
} else {
if info.Size() > 0 && filepath.Ext(path) == dataFileFormatSuffix {
exists = true
return filepath.SkipDir
}
}
return nil
})
return exists, err
}
91 changes: 91 additions & 0 deletions path_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package storage

import (
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestValidatePathFormat(t *testing.T) {
tests := []struct {
path string
hasErr bool
}{
{"", true},
{"path", true},
{"path/", false},
}

for _, test := range tests {
err := validatePathFormat(test.path)
if test.hasErr {
assert.Error(t, err, "Expected an error for path '%s'", test.path)
} else {
assert.NoError(t, err, "Expected no error for path '%s'", test.path)
}
}
}

func TestEnsureDataDirectoryExists(t *testing.T) {
tempDir, err := os.MkdirTemp("", "test_storage")
require.NoError(t, err)
defer os.RemoveAll(tempDir)

path := filepath.Join(tempDir, "data/")
err = ensureDataDirectoryExists(path)
require.NoError(t, err, "Failed to ensure directory exists: %v", err)

_, err = os.Stat(path)
require.NoError(t, err, "Directory was not created: %v", err)

assert.True(t, isDir(path), "Path is not a directory")
}

func TestValidateWriteAccess(t *testing.T) {
tempDir, err := os.MkdirTemp("", "test_storage")
require.NoError(t, err)
defer os.RemoveAll(tempDir)

err = validateWriteAccess(tempDir + "/")
assert.NoError(t, err, "Failed to test write access: %v", err)
}

func TestDataFileExists(t *testing.T) {
tempDir, err := os.MkdirTemp("", "test_storage")
require.NoError(t, err)
defer os.RemoveAll(tempDir)

dataFilePath := filepath.Join(tempDir, "data"+dataFileFormatSuffix)
dataFile, err := os.OpenFile(dataFilePath, os.O_CREATE|os.O_WRONLY, 0o644)
_, err = dataFile.Write([]byte("test"))
require.NoError(t, err, "Failed to write to test .dat file: %v", err)

exists, err := dataFileExists(tempDir)
require.NoError(t, err, "Failed to check if data file exists: %v", err)
assert.True(t, exists, "Expected data file to exist")
}

func TestDataFileNotExists(t *testing.T) {
tempDir, err := os.MkdirTemp("", "test_storage")
require.NoError(t, err)
defer os.RemoveAll(tempDir)

dataFilePath := filepath.Join(tempDir, "data"+dataFileFormatSuffix)
_, err = os.Create(dataFilePath)
require.NoError(t, err, "Failed to create test .dat file: %v", err)

exists, err := dataFileExists(tempDir)
require.NoError(t, err, "Failed to check if data file exists: %v", err)
assert.False(t, exists, "Expected data file to exist")
}

func isDir(path string) bool {
stat, err := os.Stat(path)
if err != nil {
return false
}
return stat.IsDir()
}
19 changes: 1 addition & 18 deletions unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import (
)

const (
lockFileName = ".lock"
testAccessFileName = "test-access-file"
lockFileName = ".lock"
)

func createFlock(path string) (*os.File, error) {
Expand All @@ -23,19 +22,3 @@ func createFlock(path string) (*os.File, error) {

return lockFile, nil
}

func validateDataDirAccess(path string) error {
testFile, err := os.OpenFile(path+testAccessFileName, os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
err = testFile.Close()
if err != nil {
return err
}
err = os.Remove(path + testAccessFileName)
if err != nil {
return err
}
return nil
}

0 comments on commit b2dfb45

Please sign in to comment.