This repository has been archived by the owner on Dec 4, 2021. It is now read-only.
/
remove.go
147 lines (112 loc) · 2.68 KB
/
remove.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
package king
import (
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/illiliti/king/internal/hash"
"github.com/illiliti/king/etcsums"
"github.com/illiliti/king/manifest"
)
// TODO unit tests
// TODO better docs
var ErrRemoveUnresolvedDependencies = errors.New("other packages depend on target")
// RemoveOptions provides facilities for removing package.
type RemoveOptions struct {
// NoCheckReverseDependencies forcefully removes package even if other
// installed packages depends on it.
NoCheckReverseDependencies bool
// RemoveEtcFiles forcefully removes /etc/* files without special handling
RemoveEtcFiles bool
}
// Remove purges package from the system and gracefully swaps dangling
// alternatives if they are exist.
func (p *Package) Remove(ro *RemoveOptions) error {
if !ro.NoCheckReverseDependencies {
if err := unresolvedDependencies(p); err != nil {
return err
}
}
mf, es, err := openManifestEtcsums(p.Path, ro.RemoveEtcFiles, false)
if err != nil {
return err
}
if err := swapAlternatives(p, mf); err != nil {
return fmt.Errorf("swap alternatives: %w", err)
}
defer p.cfg.ResetOwnedPaths()
defer p.cfg.ResetReverseDependencies()
for _, r := range mf.Sort(manifest.Files) {
if err := remove(es, p.cfg.RootDir, r); err != nil {
return err
}
}
return nil
}
func unresolvedDependencies(p *Package) error {
dd, err := p.ReverseDependencies()
if errors.Is(err, ErrReverseDependenciesNotFound) {
return nil
}
if err != nil {
return err
}
return fmt.Errorf("remove package %s: %w: %s", p.Name, ErrRemoveUnresolvedDependencies, dd)
}
func swapAlternatives(p *Package, mf *manifest.Manifest) error {
for _, r := range mf.Sort(manifest.NoSort) {
if strings.HasSuffix(r, "/") {
continue
}
a, err := NewAlternative(p.cfg, &AlternativeOptions{
Path: r,
})
if errors.Is(err, ErrAlternativeNotFound) {
continue
}
if err != nil {
return err
}
if a.Name == p.Name {
continue
}
if _, err := a.Swap(); err != nil {
return err
}
}
if err := mf.Rehash(); err != nil {
return err
}
return mf.Close()
}
func remove(es *etcsums.Etcsums, r, p string) error {
rp := filepath.Join(r, p)
st, err := os.Lstat(rp)
if errors.Is(err, os.ErrNotExist) {
return nil
}
if err != nil {
return err
}
isEmpty := func() bool {
f, _ := os.Open(rp) // TODO panic on error
defer f.Close()
_, err := f.ReadDir(1)
return errors.Is(err, io.EOF)
}
switch {
case st.IsDir() && !isEmpty():
return nil
case es != nil && st.Mode().IsRegular() && strings.HasPrefix(p, "/etc/"):
x, err := hash.Sha256(rp)
if err != nil {
return err
}
if !es.Has(x) {
return nil
}
}
return os.Remove(rp)
}