master
go 82 lines 1.74 KB
Raw
1 package fsutil
2
3 import (
4 "errors"
5 "fmt"
6 "io/fs"
7 "os"
8 "path/filepath"
9 )
10
11 // DirWritable checks if a directory is writable. If the directory does
12 // not exist it is created with writable permission.
13 func DirWritable(dir string) error {
14 if dir == "" {
15 return errors.New("directory not specified")
16 }
17
18 var err error
19 dir, err = ExpandHome(dir)
20 if err != nil {
21 return err
22 }
23
24 fi, err := os.Stat(dir)
25 if err != nil {
26 if errors.Is(err, fs.ErrNotExist) {
27 // Directory does not exist, so create it.
28 err = os.Mkdir(dir, 0775)
29 if err == nil {
30 return nil
31 }
32 }
33 if errors.Is(err, fs.ErrPermission) {
34 err = fs.ErrPermission
35 }
36 return fmt.Errorf("directory not writable: %s: %w", dir, err)
37 }
38 if !fi.IsDir() {
39 return fmt.Errorf("not a directory: %s", dir)
40 }
41
42 // Directory exists, check that a file can be written.
43 file, err := os.CreateTemp(dir, "writetest")
44 if err != nil {
45 if errors.Is(err, fs.ErrPermission) {
46 err = fs.ErrPermission
47 }
48 return fmt.Errorf("directory not writable: %s: %w", dir, err)
49 }
50 file.Close()
51 return os.Remove(file.Name())
52 }
53
54 // ExpandHome expands the path to include the home directory if the path is
55 // prefixed with `~`. If it isn't prefixed with `~`, the path is returned
56 // as-is.
57 func ExpandHome(path string) (string, error) {
58 if path == "" {
59 return path, nil
60 }
61
62 if path[0] != '~' {
63 return path, nil
64 }
65
66 if len(path) > 1 && path[1] != '/' && path[1] != '\\' {
67 return "", errors.New("cannot expand user-specific home dir")
68 }
69
70 dir, err := os.UserHomeDir()
71 if err != nil {
72 return "", err
73 }
74
75 return filepath.Join(dir, path[1:]), nil
76 }
77
78 // FileExists return true if the file exists
79 func FileExists(filename string) bool {
80 _, err := os.Lstat(filename)
81 return !errors.Is(err, os.ErrNotExist)
82 }