aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/system/tmpfiles.go
blob: 7300f30ae4b0fb41c8ada6d1e1dd67c336e81f57 (plain)
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
package system

import (
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"

	"git.ophivana.moe/security/fortify/acl"
	"git.ophivana.moe/security/fortify/internal/fmsg"
)

// CopyFile registers an Op that copies path dst from src.
func (sys *I) CopyFile(dst, src string) *I {
	return sys.CopyFileType(Process, dst, src)
}

// CopyFileType registers a file copying Op labelled with type et.
func (sys *I) CopyFileType(et Enablement, dst, src string) *I {
	sys.lock.Lock()
	sys.ops = append(sys.ops, &Tmpfile{et, tmpfileCopy, dst, src})
	sys.lock.Unlock()

	sys.UpdatePermType(et, dst, acl.Read)

	return sys
}

// Link registers an Op that links dst to src.
func (sys *I) Link(oldname, newname string) *I {
	return sys.LinkFileType(Process, oldname, newname)
}

// LinkFileType registers a file linking Op labelled with type et.
func (sys *I) LinkFileType(et Enablement, oldname, newname string) *I {
	sys.lock.Lock()
	defer sys.lock.Unlock()

	sys.ops = append(sys.ops, &Tmpfile{et, tmpfileLink, newname, oldname})

	return sys
}

// Write registers an Op that writes dst with the contents of src.
func (sys *I) Write(dst, src string) *I {
	return sys.WriteType(Process, dst, src)
}

// WriteType registers a file writing Op labelled with type et.
func (sys *I) WriteType(et Enablement, dst, src string) *I {
	sys.lock.Lock()
	sys.ops = append(sys.ops, &Tmpfile{et, tmpfileWrite, dst, src})
	sys.lock.Unlock()

	sys.UpdatePermType(et, dst, acl.Read)

	return sys
}

const (
	tmpfileCopy uint8 = iota
	tmpfileLink
	tmpfileWrite
)

type Tmpfile struct {
	et       Enablement
	method   uint8
	dst, src string
}

func (t *Tmpfile) Type() Enablement {
	return t.et
}

func (t *Tmpfile) apply(_ *I) error {
	switch t.method {
	case tmpfileCopy:
		fmsg.VPrintln("publishing tmpfile", t)
		return fmsg.WrapErrorSuffix(copyFile(t.dst, t.src),
			fmt.Sprintf("cannot copy tmpfile %q:", t.dst))
	case tmpfileLink:
		fmsg.VPrintln("linking tmpfile", t)
		return fmsg.WrapErrorSuffix(os.Link(t.src, t.dst),
			fmt.Sprintf("cannot link tmpfile %q:", t.dst))
	case tmpfileWrite:
		fmsg.VPrintln("writing", t)
		return fmsg.WrapErrorSuffix(os.WriteFile(t.dst, []byte(t.src), 0600),
			fmt.Sprintf("cannot write tmpfile %q:", t.dst))
	default:
		panic("invalid tmpfile method " + strconv.Itoa(int(t.method)))
	}
}

func (t *Tmpfile) revert(_ *I, ec *Criteria) error {
	if ec.hasType(t) {
		fmsg.VPrintf("removing tmpfile %q", t.dst)
		return fmsg.WrapErrorSuffix(os.Remove(t.dst),
			fmt.Sprintf("cannot remove tmpfile %q:", t.dst))
	} else {
		fmsg.VPrintf("skipping tmpfile %q", t.dst)
		return nil
	}
}

func (t *Tmpfile) Is(o Op) bool {
	t0, ok := o.(*Tmpfile)
	return ok && t0 != nil && *t == *t0
}

func (t *Tmpfile) Path() string {
	if t.method == tmpfileWrite {
		return fmt.Sprintf("(%d bytes of data)", len(t.src))
	}
	return t.src
}

func (t *Tmpfile) String() string {
	switch t.method {
	case tmpfileCopy:
		return fmt.Sprintf("%q from %q", t.dst, t.src)
	case tmpfileLink:
		return fmt.Sprintf("%q from %q", t.dst, t.src)
	case tmpfileWrite:
		return fmt.Sprintf("%d bytes of data to %q", len(t.src), t.dst)
	default:
		panic("invalid tmpfile method " + strconv.Itoa(int(t.method)))
	}
}

func copyFile(dst, src string) error {
	dstD, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}

	srcD, err := os.Open(src)
	if err != nil {
		return errors.Join(err, dstD.Close())
	}

	_, err = io.Copy(dstD, srcD)
	return errors.Join(err, dstD.Close(), srcD.Close())
}