-
-
Notifications
You must be signed in to change notification settings - Fork 480
/
fsck.go
158 lines (134 loc) · 3.54 KB
/
fsck.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package fs
import (
"context"
"os"
"path/filepath"
"sort"
"strings"
"syscall"
"github.com/gopasspw/gopass/internal/out"
"github.com/gopasspw/gopass/pkg/ctxutil"
"github.com/gopasspw/gopass/pkg/debug"
"github.com/gopasspw/gopass/pkg/fsutil"
"github.com/gopasspw/gopass/pkg/termio"
)
// Fsck checks the storage integrity
func (s *Store) Fsck(ctx context.Context) error {
pcb := ctxutil.GetProgressCallback(ctx)
entries, err := s.List(ctx, "")
if err != nil {
return err
}
dirs := make(map[string]struct{}, len(entries))
for _, entry := range entries {
pcb()
debug.Log("checking entry %q", entry)
filename := filepath.Join(s.path, entry)
dirs[filepath.Dir(filename)] = struct{}{}
if err := s.fsckCheckFile(ctx, filename); err != nil {
return err
}
}
for dir := range dirs {
debug.Log("checking dir %q", dir)
if err := s.fsckCheckDir(ctx, dir); err != nil {
return err
}
}
if err := s.fsckCheckEmptyDirs(); err != nil {
return err
}
debug.Log("checking root dir %q", s.path)
if err := s.fsckCheckDir(ctx, s.path); err != nil {
return err
}
debug.Log("checking git config")
return s.InitConfig(ctx, termio.DetectName(ctx, nil), termio.DetectEmail(ctx, nil))
}
func (s *Store) fsckCheckFile(ctx context.Context, filename string) error {
fi, err := os.Stat(filename)
if err != nil {
return err
}
if fi.Mode().Perm()&0177 == 0 {
return nil
}
out.Printf(ctx, "Permissions too wide: %s (%s)", filename, fi.Mode().String())
np := uint32(fi.Mode().Perm() & 0600)
out.Printf(ctx, " Fixing permissions from %s to %s", fi.Mode().Perm().String(), os.FileMode(np).Perm().String())
if err := syscall.Chmod(filename, np); err != nil {
out.Errorf(ctx, " Failed to set permissions for %s to rw-------: %s", filename, err)
}
return nil
}
func (s *Store) fsckCheckDir(ctx context.Context, dirname string) error {
fi, err := os.Stat(dirname)
if err != nil {
return err
}
// check if any group or other perms are set,
// i.e. check for perms other than rwx------
if fi.Mode().Perm()&077 != 0 {
out.Printf(ctx, "Permissions too wide %s on dir %s", fi.Mode().Perm().String(), dirname)
np := uint32(fi.Mode().Perm() & 0700)
out.Printf(ctx, " Fixing permissions from %s to %s", fi.Mode().Perm().String(), os.FileMode(np).Perm().String())
if err := syscall.Chmod(dirname, np); err != nil {
out.Errorf(ctx, " Failed to set permissions for %s to rwx------: %s", dirname, err)
}
}
// check for empty folders
isEmpty, err := fsutil.IsEmptyDir(dirname)
if err != nil {
return err
}
if isEmpty {
out.Errorf(ctx, "Folder %s is empty. Removing", dirname)
return os.Remove(dirname)
}
return nil
}
func (s *Store) fsckCheckEmptyDirs() error {
v := []string{}
if err := filepath.Walk(s.path, func(fp string, fi os.FileInfo, ferr error) error {
if ferr != nil {
return ferr
}
if !fi.IsDir() {
return nil
}
if strings.HasPrefix(fi.Name(), ".") {
return filepath.SkipDir
}
if fp == s.path {
return nil
}
// add candidate
debug.Log("adding candidate %q", fp)
v = append(v, fp)
return nil
}); err != nil {
return err
}
// start with longest path (deepest dir)
sort.Slice(v, func(i, j int) bool {
return len(v[i]) > len(v[j])
})
for _, d := range v {
if err := fsckRemoveEmptyDir(d); err != nil {
return err
}
}
return nil
}
func fsckRemoveEmptyDir(fp string) error {
ls, err := os.ReadDir(fp)
if err != nil {
return err
}
if len(ls) > 0 {
debug.Log("dir %q is not empty (%d)", fp, len(ls))
return nil
}
debug.Log("removing %q ...", fp)
return os.Remove(fp)
}