mirror of
https://github.com/outbackdingo/debos.git
synced 2026-01-27 10:18:47 +00:00
157 lines
3.6 KiB
Go
157 lines
3.6 KiB
Go
/*
|
|
Raw Action
|
|
|
|
Directly write a file to the output image at a given offset.
|
|
This is typically useful for bootloaders.
|
|
|
|
# Yaml syntax:
|
|
- action: raw
|
|
origin: name
|
|
source: filename
|
|
offset: bytes
|
|
|
|
Mandatory properties:
|
|
|
|
- source -- the name of file to be written into the output image.
|
|
|
|
Optional properties:
|
|
|
|
- origin -- reference to named file or directory.
|
|
If not provided, defaults to the recipe directory.
|
|
|
|
- offset -- offset in bytes or in sector number e.g 256s.
|
|
The sector size is either the recipe header 'sectorsize' or the default 512 sector
|
|
size.
|
|
Internal templating mechanism will append the 's' suffix, for instance: '{{ sector 256 }}' will be converted to '256s'.
|
|
Deprecated, use '256s' instead of '{{ sector 256 }}'.
|
|
The default value is zero.
|
|
|
|
- partition -- named partition to write to
|
|
*/
|
|
package actions
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"path"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/go-debos/debos"
|
|
)
|
|
|
|
type RawAction struct {
|
|
debos.BaseAction `yaml:",inline"`
|
|
Origin string // there the source comes from
|
|
Offset string
|
|
Source string // relative path inside of origin
|
|
Path string // deprecated option (for backward compatibility)
|
|
Partition string // Partition to write otherwise full image
|
|
}
|
|
|
|
func (raw *RawAction) checkDeprecatedSyntax() error {
|
|
// New syntax is based on 'origin' and 'source'
|
|
// Check if we do not mix new and old syntax
|
|
// TODO: remove deprecated syntax verification
|
|
if len(raw.Path) > 0 {
|
|
// Deprecated syntax based on 'source' and 'path'
|
|
log.Printf("Usage of 'source' and 'path' properties is deprecated.")
|
|
log.Printf("Please use 'origin' and 'source' properties.")
|
|
if len(raw.Origin) > 0 {
|
|
return errors.New("can't mix 'origin' and 'path'(deprecated option) properties")
|
|
}
|
|
if len(raw.Source) == 0 {
|
|
return errors.New("'source' and 'path' properties can't be empty")
|
|
}
|
|
// Switch to new syntax
|
|
raw.Origin = raw.Source
|
|
raw.Source = raw.Path
|
|
raw.Path = ""
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (raw *RawAction) Verify(_ *debos.Context) error {
|
|
if err := raw.checkDeprecatedSyntax(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(raw.Source) == 0 {
|
|
return errors.New("'source' property can't be empty")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (raw *RawAction) Run(context *debos.Context) error {
|
|
origin := context.RecipeDir
|
|
|
|
if len(raw.Origin) > 0 {
|
|
var found bool
|
|
if origin, found = context.Origin(raw.Origin); !found {
|
|
return fmt.Errorf("origin `%s` doesn't exist", raw.Origin)
|
|
}
|
|
}
|
|
|
|
s := path.Join(origin, raw.Source)
|
|
content, err := os.ReadFile(s)
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to read %s", s)
|
|
}
|
|
|
|
var devicePath string
|
|
if raw.Partition != "" {
|
|
for _, p := range context.ImagePartitions {
|
|
if p.Name == raw.Partition {
|
|
devicePath = p.DevicePath
|
|
break
|
|
}
|
|
}
|
|
|
|
if devicePath == "" {
|
|
return fmt.Errorf("failed to find partition named %s", raw.Partition)
|
|
}
|
|
} else {
|
|
devicePath = context.Image
|
|
}
|
|
|
|
target, err := os.OpenFile(devicePath, os.O_WRONLY, 0)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to open %s: %w", devicePath, err)
|
|
}
|
|
defer target.Close()
|
|
|
|
var offset int64
|
|
if len(raw.Offset) > 0 {
|
|
sector := false
|
|
offs := raw.Offset
|
|
if strings.HasSuffix(offs, "s") {
|
|
sector = true
|
|
offs = strings.TrimSuffix(offs, "s")
|
|
}
|
|
offset, err = strconv.ParseInt(offs, 0, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't parse offset %w", err)
|
|
}
|
|
|
|
if sector {
|
|
offset = offset * int64(context.SectorSize)
|
|
}
|
|
}
|
|
|
|
bytes, err := target.WriteAt(content, offset)
|
|
if bytes != len(content) {
|
|
return fmt.Errorf("couldn't write complete data %w", err)
|
|
}
|
|
|
|
err = target.Sync()
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't sync content %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|