diff --git a/assigner/config/config.go b/assigner/config/config.go index 963438770..3a9bc12be 100644 --- a/assigner/config/config.go +++ b/assigner/config/config.go @@ -7,7 +7,7 @@ import ( "path/filepath" sticfg "github.com/ipni/storetheindex/config" - "github.com/mitchellh/go-homedir" + "github.com/ipni/storetheindex/fsutil" ) // Config is used to load config files. @@ -65,7 +65,7 @@ func PathRoot() (string, error) { if dir != "" { return dir, nil } - return homedir.Expand(DefaultPathRoot) + return fsutil.ExpandHome(DefaultPathRoot) } // Load reads the json-serialized config at the specified path. diff --git a/config/config.go b/config/config.go index 5c646f3ba..00a49c588 100644 --- a/config/config.go +++ b/config/config.go @@ -7,7 +7,7 @@ import ( "os" "path/filepath" - "github.com/mitchellh/go-homedir" + "github.com/ipni/storetheindex/fsutil" ) // Config is used to load config files. @@ -74,7 +74,7 @@ func PathRoot() (string, error) { if dir != "" { return dir, nil } - return homedir.Expand(DefaultPathRoot) + return fsutil.ExpandHome(DefaultPathRoot) } // Load reads the json-serialized config at the specified path. diff --git a/fsutil/fsutil.go b/fsutil/fsutil.go index bf7b1f106..c844a8230 100644 --- a/fsutil/fsutil.go +++ b/fsutil/fsutil.go @@ -3,10 +3,10 @@ package fsutil import ( "errors" "fmt" + "io/fs" "os" + "path/filepath" "time" - - "github.com/mitchellh/go-homedir" ) // DirWritable checks if a directory is writable. If the directory does @@ -17,37 +17,64 @@ func DirWritable(dir string) error { } var err error - dir, err = homedir.Expand(dir) + dir, err = ExpandHome(dir) if err != nil { return err } - - if _, err = os.Stat(dir); err != nil { - if errors.Is(err, os.ErrNotExist) { - // dir doesn't exist, check that we can create it - err = os.Mkdir(dir, 0o775) + fi, err := os.Stat(dir) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + // Directory does not exist, so create it. + err = os.Mkdir(dir, 0775) if err == nil { return nil } } - if errors.Is(err, os.ErrPermission) { - err = os.ErrPermission + if errors.Is(err, fs.ErrPermission) { + err = fs.ErrPermission } - return fmt.Errorf("cannot write to %s: %w", dir, err) + return fmt.Errorf("directory not writable: %s: %w", dir, err) + } + if !fi.IsDir() { + return fmt.Errorf("not a directory: %s", dir) } - // dir exists, make sure we can write to it - file, err := os.CreateTemp(dir, "test") + // Directory exists, check that a file can be written. + file, err := os.CreateTemp(dir, "writetest") if err != nil { - if errors.Is(err, os.ErrPermission) { - err = os.ErrPermission + if errors.Is(err, fs.ErrPermission) { + err = fs.ErrPermission } - return fmt.Errorf("cannot write to %s: %w", dir, err) + return fmt.Errorf("directory not writable: %s: %w", dir, err) } file.Close() return os.Remove(file.Name()) } +// ExpandHome expands the path to include the home directory if the path is +// prefixed with `~`. If it isn't prefixed with `~`, the path is returned +// as-is. +func ExpandHome(path string) (string, error) { + if path == "" { + return path, nil + } + + if path[0] != '~' { + return path, nil + } + + if len(path) > 1 && path[1] != '/' && path[1] != '\\' { + return "", errors.New("cannot expand user-specific home dir") + } + + dir, err := os.UserHomeDir() + if err != nil { + return "", err + } + + return filepath.Join(dir, path[1:]), nil +} + // FileChanged returns the modification time of a file and true if different // from the given time. func FileChanged(filePath string, modTime time.Time) (time.Time, bool, error) { diff --git a/fsutil/fsutil_test.go b/fsutil/fsutil_test.go index fcf4fddcb..b67844692 100644 --- a/fsutil/fsutil_test.go +++ b/fsutil/fsutil_test.go @@ -80,3 +80,36 @@ func TestFileExists(t *testing.T) { require.True(t, fsutil.FileExists(fileName)) } + +func TestExpandHome(t *testing.T) { + dir, err := fsutil.ExpandHome("") + require.NoError(t, err) + require.Equal(t, "", dir) + + origDir := filepath.Join("somedir", "somesub") + dir, err = fsutil.ExpandHome(origDir) + require.NoError(t, err) + require.Equal(t, origDir, dir) + + _, err = fsutil.ExpandHome(filepath.FromSlash("~nosuchuser/somedir")) + require.Error(t, err) + + homeEnv := "HOME" + if runtime.GOOS == "windows" { + homeEnv = "USERPROFILE" + } + origHome := os.Getenv(homeEnv) + defer os.Setenv(homeEnv, origHome) + homeDir := filepath.Join(t.TempDir(), "testhome") + os.Setenv(homeEnv, homeDir) + + const subDir = "mytmp" + origDir = filepath.Join("~", subDir) + dir, err = fsutil.ExpandHome(origDir) + require.NoError(t, err) + require.Equal(t, filepath.Join(homeDir, subDir), dir) + + os.Unsetenv(homeEnv) + _, err = fsutil.ExpandHome(origDir) + require.Error(t, err) +} diff --git a/go.mod b/go.mod index f1fc13a91..30cfd9949 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,6 @@ require ( github.com/ipni/go-libipni v0.5.25 github.com/libp2p/go-libp2p v0.36.3 github.com/libp2p/go-msgio v0.3.0 - github.com/mitchellh/go-homedir v1.1.0 github.com/multiformats/go-multiaddr v0.13.0 github.com/multiformats/go-multicodec v0.9.0 github.com/multiformats/go-multihash v0.2.3 diff --git a/go.sum b/go.sum index 7daf78213..ab1440217 100644 --- a/go.sum +++ b/go.sum @@ -581,8 +581,6 @@ github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1 github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc=