summaryrefslogtreecommitdiff
path: root/actions/raw_action.go
blob: d3e66fe9c3c313d551410919b0e47d2e7e653c52 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/*
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:

- origin -- reference to named file or directory.

- source -- the name of file located in 'origin' to be written into the output image.

Optional properties:

- offset -- offset in bytes for output image file.
It is possible to use internal templating mechanism of debos to calculate offset
with sectors (512 bytes) instead of bytes, for instance: '{{ sector 256 }}'.
The default value is zero.

- partition -- named partition to write to
*/
package actions

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strconv"

	"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(context *debos.DebosContext) error {
	if err := raw.checkDeprecatedSyntax(); err != nil {
		return err
	}

	if len(raw.Origin) == 0 || len(raw.Source) == 0 {
		return errors.New("'origin' and 'source' properties can't be empty")
	}

	return nil
}

func (raw *RawAction) Run(context *debos.DebosContext) error {
	raw.LogStart()
	origin, found := context.Origins[raw.Origin]
	if !found {
		return fmt.Errorf("Origin `%s` doesn't exist\n", raw.Origin)
	}
	s := path.Join(origin, raw.Source)
	content, err := ioutil.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: %v", devicePath, err)
	}

	offset, err := strconv.ParseInt(raw.Offset, 0, 64)
	if err != nil {
		return fmt.Errorf("Couldn't parse offset %v", err)
	}
	bytes, err := target.WriteAt(content, offset)
	if bytes != len(content) {
		return errors.New("Couldn't write complete data")
	}

	return nil
}