diff --git a/actions/image_partition_action.go b/actions/image_partition_action.go index 442168bd..b7bf5f26 100644 --- a/actions/image_partition_action.go +++ b/actions/image_partition_action.go @@ -60,12 +60,8 @@ unique. 'none' fs type should be used for partition without filesystem. -- start -- offset from beginning of the disk there the partition starts. - -- end -- offset from beginning of the disk there the partition ends. - -For 'start' and 'end' properties offset can be written in human readable -form -- '32MB', '1GB' or as disk percentage -- '100%'. +- end -- offset from beginning of the disk there the partition ends or +offset from beginning of where the partition starts specified with prefix "+" Optional properties: @@ -80,6 +76,11 @@ GPT sets the partition type to Linux Swap. For msdos partition types hex codes see: https://en.wikipedia.org/wiki/Partition_type For gpt partition type GUIDs see: https://systemd.io/DISCOVERABLE_PARTITIONS/ +- start -- offset from beginning of the disk there the partition starts. + +For 'start' and 'end' properties offset can be written in human readable +form -- '32MB', '1GB' or as disk percentage -- '100%'. + - features -- list of additional filesystem features which need to be enabled for partition. @@ -157,6 +158,7 @@ import ( "path" "path/filepath" "sort" + "strconv" "strings" "syscall" "time" @@ -584,6 +586,61 @@ func (i ImagePartitionAction) PostMachineCleanup(context *debos.DebosContext) er return nil } +func ParseOffset(offset string) (int, string, error) { + /* Extract value of offset (int) and type from string*/ + var v, t []rune + for _, l := range offset { + switch { + case l >= '0' && l <= '9': + v = append(v, l) + case l >= 'A' && l <= 'Z', l >= 'a' && l <= 'z', l == '%': + t = append(t, l) + } + } + + val, err := strconv.Atoi(string(v)) + typ := string(t) + + if err != nil { + return 0, "", fmt.Errorf("Can not parse %s to integer", string(v)) + } + + return val, typ, nil +} + +func CalculateOffset(start, end string) (string, error) { + /* Do addition if end value uses a '+' prefix */ + if strings.HasPrefix(end, "+") { + end = strings.Split(end, "+")[1] + valEnd, typeEnd, errStart := ParseOffset(end) + valStart, typeStart, errEnd := ParseOffset(start) + + if typeStart != typeEnd { + return "", fmt.Errorf("Relative offset types are not consistent") + } + + if errStart != nil { + return "", errStart + } else if errEnd != nil { + return "", errEnd + } + + end = strconv.Itoa(valStart+valEnd) + typeEnd + } + return end, nil +} + +func ValidPercentage(offset string) error { + val, typ, err := ParseOffset(offset) + if err != nil { + return err + } + if typ == "%" && val > 100 { + return fmt.Errorf("Size can not exceed 100%%") + } + return nil +} + func (i *ImagePartitionAction) Verify(context *debos.DebosContext) error { if len(i.GptGap) > 0 { log.Println("WARNING: special version of parted is needed for 'gpt_gap' option") @@ -598,7 +655,9 @@ func (i *ImagePartitionAction) Verify(context *debos.DebosContext) error { } num := 1 + prevEnd := "0%" for idx, _ := range i.Partitions { + var err error p := &i.Partitions[idx] p.number = num num++ @@ -642,12 +701,30 @@ func (i *ImagePartitionAction) Verify(context *debos.DebosContext) error { } if p.Start == "" { - return fmt.Errorf("Partition %s missing start", p.Name) + p.Start = prevEnd + } + + err = ValidPercentage(p.Start) + if err != nil { + return err } + if p.End == "" { return fmt.Errorf("Partition %s missing end", p.Name) } + p.End, err = CalculateOffset(p.Start, p.End) + if err != nil { + return err + } + + err = ValidPercentage(p.End) + if err != nil { + return err + } + + prevEnd = p.End + switch p.FS { case "fat32": p.FS = "vfat"