59 lines
1.6 KiB
Go
59 lines
1.6 KiB
Go
package inject
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
var setFileMode = os.Chmod
|
|
|
|
// Injector writes node config into a mounted image (boot/root paths supplied by caller).
|
|
type Injector struct {
|
|
BootPath string
|
|
RootPath string
|
|
}
|
|
|
|
// FileSpec describes a file to write.
|
|
type FileSpec struct {
|
|
Path string
|
|
Content []byte
|
|
Mode os.FileMode
|
|
RootFS bool // if true, write under root path; else boot path
|
|
Symlink string // relative target for native systemd enablement
|
|
}
|
|
|
|
// Write materializes the requested files under the boot or root mount because
|
|
// the burn flow needs a single place to stage config fragments before sync.
|
|
func (i *Injector) Write(files []FileSpec) error {
|
|
for _, f := range files {
|
|
base := i.BootPath
|
|
if f.RootFS {
|
|
base = i.RootPath
|
|
}
|
|
target := filepath.Join(base, f.Path)
|
|
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
|
|
return fmt.Errorf("mkdir %s: %w", filepath.Dir(target), err)
|
|
}
|
|
if f.Symlink != "" {
|
|
if current, err := os.Readlink(target); err == nil && current == f.Symlink {
|
|
continue
|
|
}
|
|
// Refuse to replace unrelated files in the mounted image.
|
|
if err := os.Symlink(f.Symlink, target); err != nil {
|
|
return fmt.Errorf("link %s: %w", target, err)
|
|
}
|
|
continue
|
|
}
|
|
if err := os.WriteFile(target, f.Content, f.Mode); err != nil {
|
|
return fmt.Errorf("write %s: %w", target, err)
|
|
}
|
|
// WriteFile retains an existing file's permissions, including loose
|
|
// permissions inherited from a recovery image.
|
|
if err := setFileMode(target, f.Mode); err != nil {
|
|
return fmt.Errorf("chmod %s: %w", target, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|