system: separate link Op implementation
All checks were successful
Test / Create distribution (push) Successful in 24s
Test / Run NixOS test (push) Successful in 2m13s

This Op would still be useful after replacing the Tmpfiles interface, so isolate it here.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-02-16 12:15:26 +09:00
parent 90b86a5531
commit c667b13a00
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
3 changed files with 55 additions and 28 deletions

53
internal/system/link.go Normal file
View File

@ -0,0 +1,53 @@
package system
import (
"fmt"
"os"
"git.gensokyo.uk/security/fortify/internal/fmsg"
)
// 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, &Hardlink{et, newname, oldname})
return sys
}
type Hardlink struct {
et Enablement
dst, src string
}
func (l *Hardlink) Type() Enablement { return l.et }
func (l *Hardlink) apply(_ *I) error {
fmsg.VPrintln("linking ", l)
return fmsg.WrapErrorSuffix(os.Link(l.src, l.dst),
fmt.Sprintf("cannot link %q:", l.dst))
}
func (l *Hardlink) revert(_ *I, ec *Criteria) error {
if ec.hasType(l) {
fmsg.VPrintf("removing hard link %q", l.dst)
return fmsg.WrapErrorSuffix(os.Remove(l.dst),
fmt.Sprintf("cannot remove hard link %q:", l.dst))
} else {
fmsg.VPrintf("skipping hard link %q", l.dst)
return nil
}
}
func (l *Hardlink) Is(o Op) bool {
l0, ok := o.(*Hardlink)
return ok && l0 != nil && *l == *l0
}
func (l *Hardlink) Path() string { return l.src }
func (l *Hardlink) String() string { return fmt.Sprintf("%q from %q", l.dst, l.src) }

View File

@ -27,24 +27,8 @@ func (sys *I) CopyFileType(et Enablement, dst, src string) *I {
return sys 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
}
const ( const (
tmpfileCopy uint8 = iota tmpfileCopy uint8 = iota
tmpfileLink
) )
type Tmpfile struct { type Tmpfile struct {
@ -63,10 +47,6 @@ func (t *Tmpfile) apply(_ *I) error {
fmsg.VPrintln("publishing tmpfile", t) fmsg.VPrintln("publishing tmpfile", t)
return fmsg.WrapErrorSuffix(copyFile(t.dst, t.src), return fmsg.WrapErrorSuffix(copyFile(t.dst, t.src),
fmt.Sprintf("cannot copy tmpfile %q:", t.dst)) 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))
default: default:
panic("invalid tmpfile method " + strconv.Itoa(int(t.method))) panic("invalid tmpfile method " + strconv.Itoa(int(t.method)))
} }
@ -94,8 +74,6 @@ func (t *Tmpfile) String() string {
switch t.method { switch t.method {
case tmpfileCopy: case tmpfileCopy:
return fmt.Sprintf("%q from %q", t.dst, t.src) return fmt.Sprintf("%q from %q", t.dst, t.src)
case tmpfileLink:
return fmt.Sprintf("%q from %q", t.dst, t.src)
default: default:
panic("invalid tmpfile method " + strconv.Itoa(int(t.method))) panic("invalid tmpfile method " + strconv.Itoa(int(t.method)))
} }

View File

@ -57,7 +57,7 @@ func TestLink(t *testing.T) {
sys := New(150) sys := New(150)
sys.Link(tc.src, tc.dst) sys.Link(tc.src, tc.dst)
(&tcOp{Process, tc.src}).test(t, sys.ops, []Op{ (&tcOp{Process, tc.src}).test(t, sys.ops, []Op{
&Tmpfile{Process, tmpfileLink, tc.dst, tc.src}, &Hardlink{Process, tc.dst, tc.src},
}, "Link") }, "Link")
}) })
} }
@ -76,7 +76,7 @@ func TestLinkFileType(t *testing.T) {
sys := New(150) sys := New(150)
sys.LinkFileType(tc.et, tc.path, tc.dst) sys.LinkFileType(tc.et, tc.path, tc.dst)
tc.test(t, sys.ops, []Op{ tc.test(t, sys.ops, []Op{
&Tmpfile{tc.et, tmpfileLink, tc.dst, tc.path}, &Hardlink{tc.et, tc.dst, tc.path},
}, "LinkFileType") }, "LinkFileType")
}) })
} }
@ -101,10 +101,6 @@ func TestTmpfile_String(t *testing.T) {
}{ }{
{tmpfileCopy, "/tmp/fortify.1971/4b6bdc9182fb2f1d3a965c5fa8b9b66e/pulse-cookie", "/home/ophestra/xdg/config/pulse/cookie", {tmpfileCopy, "/tmp/fortify.1971/4b6bdc9182fb2f1d3a965c5fa8b9b66e/pulse-cookie", "/home/ophestra/xdg/config/pulse/cookie",
`"/tmp/fortify.1971/4b6bdc9182fb2f1d3a965c5fa8b9b66e/pulse-cookie" from "/home/ophestra/xdg/config/pulse/cookie"`}, `"/tmp/fortify.1971/4b6bdc9182fb2f1d3a965c5fa8b9b66e/pulse-cookie" from "/home/ophestra/xdg/config/pulse/cookie"`},
{tmpfileLink, "/run/user/1971/fortify/4b6bdc9182fb2f1d3a965c5fa8b9b66e/wayland", "/run/user/1971/wayland-0",
`"/run/user/1971/fortify/4b6bdc9182fb2f1d3a965c5fa8b9b66e/wayland" from "/run/user/1971/wayland-0"`},
{tmpfileLink, "/run/user/1971/fortify/4b6bdc9182fb2f1d3a965c5fa8b9b66e/pulse", "/run/user/1971/pulse/native",
`"/run/user/1971/fortify/4b6bdc9182fb2f1d3a965c5fa8b9b66e/pulse" from "/run/user/1971/pulse/native"`},
} }
for _, tc := range testCases { for _, tc := range testCases {