hst/fs: implement link fstype
All checks were successful
Test / Create distribution (push) Successful in 34s
Test / Sandbox (push) Successful in 2m16s
Test / Hpkg (push) Successful in 4m8s
Test / Sandbox (race detector) (push) Successful in 4m24s
Test / Hakurei (race detector) (push) Successful in 5m9s
Test / Hakurei (push) Successful in 2m31s
Test / Flake checks (push) Successful in 1m40s

Symlinks do not require special treatment, and doing this allows placing links in order.

Signed-off-by: Ophestra <cat@gensokyo.uk>
This commit is contained in:
Ophestra 2025-08-25 21:31:45 +09:00
parent 125f150784
commit 26cafe3e80
Signed by: cat
SSH Key Fingerprint: SHA256:gQ67O0enBZ7UdZypgtspB2FDM1g3GVw8nX0XSdcFw8Q
4 changed files with 126 additions and 0 deletions

View File

@ -95,6 +95,12 @@ func (f *FilesystemConfigJSON) MarshalJSON() ([]byte, error) {
*FSOverlay
}{fsType{FilesystemOverlay}, cv}
case *FSLink:
v = &struct {
fsType
*FSLink
}{fsType{FilesystemLink}, cv}
default:
return nil, FSImplError{f.FilesystemConfig}
}
@ -120,6 +126,9 @@ func (f *FilesystemConfigJSON) UnmarshalJSON(data []byte) error {
case FilesystemOverlay:
*f = FilesystemConfigJSON{new(FSOverlay)}
case FilesystemLink:
*f = FilesystemConfigJSON{new(FSLink)}
default:
return FSTypeError(t.Type)
}

View File

@ -70,6 +70,16 @@ func TestFilesystemConfigJSON(t *testing.T) {
}, nil,
`{"type":"overlay","dst":"/nix/store","lower":["/mnt-root/nix/.ro-store"],"upper":"/mnt-root/nix/.rw-store/upper","work":"/mnt-root/nix/.rw-store/work"}`,
`{"fs":{"type":"overlay","dst":"/nix/store","lower":["/mnt-root/nix/.ro-store"],"upper":"/mnt-root/nix/.rw-store/upper","work":"/mnt-root/nix/.rw-store/work"},"magic":3236757504}`},
{"link", hst.FilesystemConfigJSON{
FilesystemConfig: &hst.FSLink{
Target: m("/run/current-system"),
Linkname: "/run/current-system",
Dereference: true,
},
}, nil,
`{"type":"link","dst":"/run/current-system","linkname":"/run/current-system","dereference":true}`,
`{"fs":{"type":"link","dst":"/run/current-system","linkname":"/run/current-system","dereference":true},"magic":3236757504}`},
}
for _, tc := range testCases {

58
hst/fslink.go Normal file
View File

@ -0,0 +1,58 @@
package hst
import (
"encoding/gob"
"path"
"hakurei.app/container"
)
func init() { gob.Register(new(FSLink)) }
// FilesystemLink is the type string of a symbolic link.
const FilesystemLink = "link"
// FSLink represents a symlink in the container filesystem.
type FSLink struct {
// link path in container
Target *container.Absolute `json:"dst"`
// linkname the symlink points to
Linkname string `json:"linkname"`
// whether to dereference linkname before creating the link
Dereference bool `json:"dereference,omitempty"`
}
func (l *FSLink) Valid() bool {
if l == nil || l.Target == nil || l.Linkname == "" {
return false
}
return !l.Dereference || path.IsAbs(l.Linkname)
}
func (l *FSLink) Path() *container.Absolute {
if !l.Valid() {
return nil
}
return l.Target
}
func (l *FSLink) Host() []*container.Absolute { return nil }
func (l *FSLink) Apply(z *ApplyState) {
if !l.Valid() {
return
}
z.Link(l.Target, l.Linkname, l.Dereference)
}
func (l *FSLink) String() string {
if !l.Valid() {
return "<invalid>"
}
dereference := ""
if l.Dereference {
dereference = "*"
}
return "&" + l.Target.String() + ":" + dereference + l.Linkname
}

49
hst/fslink_test.go Normal file
View File

@ -0,0 +1,49 @@
package hst_test
import (
"testing"
"hakurei.app/container"
"hakurei.app/hst"
)
func TestFSLink(t *testing.T) {
checkFs(t, []fsTestCase{
{"nil", (*hst.FSLink)(nil), false, nil, nil, nil, "<invalid>"},
{"zero", new(hst.FSLink), false, nil, nil, nil, "<invalid>"},
{"deref rel", &hst.FSLink{Target: m("/"), Linkname: ":3", Dereference: true}, false, nil, nil, nil, "<invalid>"},
{"deref", &hst.FSLink{
Target: m("/run/current-system"),
Linkname: "/run/current-system",
Dereference: true,
}, true, container.Ops{
&container.SymlinkOp{
Target: m("/run/current-system"),
LinkName: "/run/current-system",
Dereference: true,
},
}, m("/run/current-system"), nil,
"&/run/current-system:*/run/current-system"},
{"direct", &hst.FSLink{
Target: m("/etc/mtab"),
Linkname: "/proc/mounts",
}, true, container.Ops{
&container.SymlinkOp{
Target: m("/etc/mtab"),
LinkName: "/proc/mounts",
},
}, m("/etc/mtab"), nil, "&/etc/mtab:/proc/mounts"},
{"direct rel", &hst.FSLink{
Target: m("/etc/mtab"),
Linkname: "../proc/mounts",
}, true, container.Ops{
&container.SymlinkOp{
Target: m("/etc/mtab"),
LinkName: "../proc/mounts",
},
}, m("/etc/mtab"), nil, "&/etc/mtab:../proc/mounts"},
})
}