carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "bytes"
5 "errors"
6 "io"
7 "os"
8 "path/filepath"
9
10 "github.com/carlosframework/platform/internal/instancectl"
11 "github.com/carlosframework/platform/internal/rootstate"
12 "github.com/carlosframework/platform/internal/safefile"
13 "golang.org/x/sys/unix"
14)
15
16type claimUnitFiles struct{ dir string }
17
18func (f claimUnitFiles) read(unit string) ([]byte, rootstate.FileIdentity, error) {
19 if !instancectl.ValidBoundClaimUnit(unit) {
20 return nil, rootstate.FileIdentity{}, os.ErrPermission
21 }
22 file, err := rootstate.OpenTrustedFile(filepath.Join(f.dir, unit))
23 if err != nil {
24 return nil, rootstate.FileIdentity{}, err
25 }
26 defer file.Close()
27 raw, err := io.ReadAll(io.LimitReader(file, rootstate.MaxRecord+1))
28 if err != nil {
29 return nil, rootstate.FileIdentity{}, err
30 }
31 if len(raw) > rootstate.MaxRecord {
32 return nil, rootstate.FileIdentity{}, os.ErrPermission
33 }
34 id, err := rootstate.PersistentIdentity(file)
35 return raw, id, err
36}
37func (f claimUnitFiles) create(unit string, body []byte) error {
38 return f.write(unit, rootstate.FileIdentity{}, body)
39}
40func (f claimUnitFiles) replace(unit string, id rootstate.FileIdentity, body []byte) error {
41 if id == (rootstate.FileIdentity{}) {
42 return os.ErrPermission
43 }
44 return f.write(unit, id, body)
45}
46func (f claimUnitFiles) write(unit string, id rootstate.FileIdentity, body []byte) error {
47 if !instancectl.ValidBoundClaimUnit(unit) {
48 return os.ErrPermission
49 }
50 checked, err := rootstate.Open(f.dir, false, true)
51 if err != nil {
52 return err
53 }
54 defer checked.Close()
55 parent, err := safefile.OpenDir(f.dir)
56 if err != nil {
57 return err
58 }
59 defer parent.Close()
60 tmp, err := os.CreateTemp(f.dir, ".claim-unit-")
61 if err != nil {
62 return err
63 }
64 name := filepath.Base(tmp.Name())
65 defer unix.Unlinkat(int(parent.Fd()), name, 0)
66 _, err = tmp.Write(body)
67 if err == nil {
68 err = tmp.Chmod(0644)
69 }
70 if err == nil {
71 err = tmp.Sync()
72 }
73 if err = errors.Join(err, tmp.Close()); err != nil {
74 return err
75 }
76 flags := uint(unix.RENAME_NOREPLACE)
77 if id != (rootstate.FileIdentity{}) {
78 _, current, err := f.read(unit)
79 if err != nil {
80 return err
81 }
82 if !current.Same(id) {
83 return os.ErrPermission
84 }
85 flags = 0
86 }
87 if err = unix.Renameat2(int(parent.Fd()), name, int(parent.Fd()), unit, flags); err != nil {
88 return err
89 }
90 if err = parent.Sync(); err != nil {
91 return err
92 }
93 got, _, err := f.read(unit)
94 if err != nil {
95 return err
96 }
97 if !bytes.Equal(got, body) {
98 return os.ErrPermission
99 }
100 return nil
101}
102
103// The flat name is only a routing projection. Root launch and containment
104// retain the binding and the private run directory independently of it.
105func projectClaimBoundSocket(s claimUnitShape) error {
106 if !claimFlatSocket(s) {
107 return nil
108 }
109 parent, err := safefile.OpenDir(filepath.Dir(s.Socket))
110 if err != nil {
111 return err
112 }
113 defer parent.Close()
114 name := filepath.Base(s.Socket)
115 target := claimSocketTarget(s)
116 if err := unix.Symlinkat(target, int(parent.Fd()), name); err != nil {
117 if !errors.Is(err, unix.EEXIST) {
118 return err
119 }
120 var buf [4096]byte
121 n, err := unix.Readlinkat(int(parent.Fd()), name, buf[:])
122 if err != nil {
123 return err
124 }
125 if string(buf[:n]) != target {
126 return os.ErrPermission
127 }
128 }
129 return parent.Sync()
130}
131