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
|
package fs
import (
"fmt"
"os"
"path/filepath"
"strings"
"unicode"
)
// JournalSpecPrefix marks a read target as a systemd journal source.
const JournalSpecPrefix = "journal:"
// ReadTargetKind identifies the backing source for a validated read target.
type ReadTargetKind int
// Valid read target kinds.
const (
FileKind ReadTargetKind = iota
JournalKind
)
// ValidatedReadTarget stores a resolved regular file path for rooted re-opens.
type ValidatedReadTarget struct {
Kind ReadTargetKind
resolvedPath string
rootedPath RootedPath
}
// IsJournalSpec reports whether spec names a journal-backed read source.
func IsJournalSpec(spec string) bool {
return strings.HasPrefix(spec, JournalSpecPrefix)
}
// NewValidatedReadTarget returns a rooted target for a resolved regular file.
func NewValidatedReadTarget(resolvedPath string) (ValidatedReadTarget, error) {
cleanedPath := filepath.Clean(resolvedPath)
if !filepath.IsAbs(cleanedPath) {
return ValidatedReadTarget{}, fmt.Errorf("validated read target requires absolute path: %s", cleanedPath)
}
info, err := os.Lstat(cleanedPath)
if err != nil {
return ValidatedReadTarget{}, fmt.Errorf("lstat validated read target %s: %w", cleanedPath, err)
}
if !info.Mode().IsRegular() {
return ValidatedReadTarget{}, fmt.Errorf("validated read target must be a regular file: %s", cleanedPath)
}
rootedPath, err := NewRootedPath(cleanedPath)
if err != nil {
return ValidatedReadTarget{}, err
}
return ValidatedReadTarget{
Kind: FileKind,
resolvedPath: cleanedPath,
rootedPath: rootedPath,
}, nil
}
// NewValidatedJournalTarget returns a validated journal-backed read target.
func NewValidatedJournalTarget(spec string) (ValidatedReadTarget, error) {
if !IsJournalSpec(spec) {
return ValidatedReadTarget{}, fmt.Errorf("journal read target requires %q prefix: %s", JournalSpecPrefix, spec)
}
if err := validateJournalSpec(spec); err != nil {
return ValidatedReadTarget{}, err
}
return ValidatedReadTarget{
Kind: JournalKind,
resolvedPath: spec,
}, nil
}
func validateJournalSpec(spec string) error {
source := strings.TrimPrefix(spec, JournalSpecPrefix)
if source == "" {
return fmt.Errorf("journal read target requires a unit name after %q", JournalSpecPrefix)
}
if strings.HasPrefix(source, "-") {
return fmt.Errorf("journal read target unit must not start with '-'")
}
for _, r := range source {
if unicode.IsControl(r) || unicode.IsSpace(r) {
return fmt.Errorf("journal read target unit contains invalid whitespace or control character")
}
}
return nil
}
// Open re-opens the validated file beneath its resolved parent directory.
func (t ValidatedReadTarget) Open() (*os.File, error) {
if t.Kind != FileKind {
return nil, fmt.Errorf("read target kind %d cannot be opened as a file", t.Kind)
}
root, err := t.rootedPath.OpenRoot()
if err != nil {
return nil, fmt.Errorf("open root for %s: %w", t.resolvedPath, err)
}
defer root.Close()
if err := t.validateEntry(root); err != nil {
return nil, err
}
fd, err := root.Open(t.rootedPath.Name())
if err != nil {
return nil, fmt.Errorf("open rooted file %s: %w", t.resolvedPath, err)
}
if err := validateOpenedFile(fd, t.resolvedPath); err != nil {
fd.Close()
return nil, err
}
if err := t.validateEntry(root); err != nil {
fd.Close()
return nil, err
}
return fd, nil
}
func (t ValidatedReadTarget) validateEntry(root *os.Root) error {
info, err := root.Lstat(t.rootedPath.Name())
if err != nil {
return fmt.Errorf("lstat rooted file %s: %w", t.resolvedPath, err)
}
if info.Mode()&os.ModeSymlink != 0 {
return fmt.Errorf("rooted file changed to symlink: %s", t.resolvedPath)
}
if !info.Mode().IsRegular() {
return fmt.Errorf("rooted file changed to non-regular file: %s", t.resolvedPath)
}
return nil
}
func validateOpenedFile(fd *os.File, resolvedPath string) error {
info, err := fd.Stat()
if err != nil {
return fmt.Errorf("stat opened file %s: %w", resolvedPath, err)
}
if !info.Mode().IsRegular() {
return fmt.Errorf("opened file is not regular: %s", resolvedPath)
}
return nil
}
|