gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/bootloader/lkenv/lkenv.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package lkenv 21 22 import ( 23 "bytes" 24 "encoding/binary" 25 "fmt" 26 "hash/crc32" 27 "os" 28 29 "golang.org/x/xerrors" 30 31 "github.com/snapcore/snapd/logger" 32 "github.com/snapcore/snapd/osutil" 33 "github.com/snapcore/snapd/strutil" 34 ) 35 36 const ( 37 SNAP_BOOTSELECT_VERSION_V1 = 0x00010001 38 SNAP_BOOTSELECT_VERSION_V2 = 0x00010010 39 ) 40 41 // const SNAP_BOOTSELECT_SIGNATURE ('S' | ('B' << 8) | ('s' << 16) | ('e' << 24)) 42 // value comes from S(Snap)B(Boot)se(select) 43 const SNAP_BOOTSELECT_SIGNATURE = 0x53 | 0x42<<8 | 0x73<<16 | 0x65<<24 44 45 // const SNAP_BOOTSELECT_RECOVERY_SIGNATURE ('S' | ('R' << 8) | ('s' << 16) | ('e' << 24)) 46 // value comes from S(Snap)R(Recovery)se(select) 47 const SNAP_BOOTSELECT_RECOVERY_SIGNATURE = 0x53 | 0x52<<8 | 0x73<<16 | 0x65<<24 48 49 // SNAP_FILE_NAME_MAX_LEN is the maximum size of a C string representing a snap name, 50 // such as for a kernel snap revision. 51 const SNAP_FILE_NAME_MAX_LEN = 256 52 53 // SNAP_BOOTIMG_PART_NUM is the number of available boot image partitions 54 const SNAP_BOOTIMG_PART_NUM = 2 55 56 // SNAP_RUN_BOOTIMG_PART_NUM is the number of available boot image partitions 57 // for uc20 for kernel/try-kernel in run mode 58 const SNAP_RUN_BOOTIMG_PART_NUM = 2 59 60 /** maximum number of available bootimg partitions for recovery systems, min 5 61 * NOTE: the number of actual bootimg partitions usable is determined by the 62 * gadget, this just sets the upper bound of maximum number of recovery systems 63 * a gadget could define without needing changes here 64 */ 65 const SNAP_RECOVERY_BOOTIMG_PART_NUM = 10 66 67 /* Default boot image file name to be used from kernel snap */ 68 const BOOTIMG_DEFAULT_NAME = "boot.img" 69 70 // for accessing the Bootimg_matrix 71 const ( 72 // the boot image partition label itself 73 MATRIX_ROW_PARTITION = 0 74 // the value of the boot image partition label mapping (i.e. the kernel 75 // revision or the recovery system label, depending on which specific 76 // matrix is being operated on) 77 MATRIX_ROW_VALUE = 1 78 ) 79 80 type Version int 81 82 const ( 83 V1 Version = iota 84 V2Run 85 V2Recovery 86 ) 87 88 // Number returns the Version of the lkenv version as it is encoded in the 89 func (v Version) Number() uint32 { 90 switch v { 91 case V1: 92 return SNAP_BOOTSELECT_VERSION_V1 93 case V2Run, V2Recovery: 94 return SNAP_BOOTSELECT_VERSION_V2 95 default: 96 panic(fmt.Sprintf("unknown lkenv version number: %v", v)) 97 } 98 } 99 100 // Signature returns the Signature of the lkenv version. 101 func (v Version) Signature() uint32 { 102 switch v { 103 case V1, V2Run: 104 return SNAP_BOOTSELECT_SIGNATURE 105 case V2Recovery: 106 return SNAP_BOOTSELECT_RECOVERY_SIGNATURE 107 default: 108 panic(fmt.Sprintf("unknown lkenv version number: %v", v)) 109 } 110 } 111 112 type envVariant interface { 113 // get returns the value of a key in the env object. 114 get(string) string 115 116 // set sets a key to a value in the env object. 117 set(string, string) 118 119 // currentCrc32 is a helper method to return the value of the crc32 stored in the 120 // environment variable - it is NOT a method to calculate the current value, 121 // it is used to store the crc32 for helper methods that validate the crc32 122 // independently of what is in the environment. 123 currentCrc32() uint32 124 // currentVersion is the same kind of helper method as currentCrc32(), 125 // always returning the value from the object itself. 126 currentVersion() uint32 127 // currentSignature is the same kind of helper method as currentCrc32(), 128 // always returning the value from the object itself. 129 currentSignature() uint32 130 131 // bootImgKernelMatrix returns the boot image matrix from the environment 132 // which stores the kernel revisions for the boot image partitions. The boot 133 // image matrix is used for various exported methods such as 134 // SetBootPartitionKernel(), etc. 135 bootImgKernelMatrix() (bootimgMatrixGeneric, error) 136 137 // bootImgRecoverySystemMatrix returns the boot image matrix from the 138 // environment which stores the recovery system labels for the boot image 139 // partitions. The boot image matrix is used for various recovery system 140 // methods such as FindFreeRecoverySystemBootPartition(), etc. 141 bootImgRecoverySystemMatrix() (bootimgMatrixGeneric, error) 142 } 143 144 var ( 145 // the variant implementations must all implement envVariant 146 _ = envVariant(&SnapBootSelect_v1{}) 147 _ = envVariant(&SnapBootSelect_v2_run{}) 148 _ = envVariant(&SnapBootSelect_v2_recovery{}) 149 ) 150 151 // Env contains the data of the little kernel environment 152 type Env struct { 153 // path is the primary lkenv object file, it can be a regular file during 154 // build time, or it can be a partition device node at run time 155 path string 156 // pathbak is the backup lkenv object file, it too can either be a regular 157 // file during build time, or a partition device node at run time, and it is 158 // typically at prepare-image time given by "<path>" + "bak", i.e. 159 // $PWD/lk.conf and $PWD/lk.confbak but will be different device nodes for 160 // different partitions at runtime. 161 pathbak string 162 // version is the configured version of the lkenv object from NewEnv. 163 version Version 164 // variant is the internal implementation of the lkenv object, dependent on 165 // the version. It is tracked separately such that we can verify a given 166 // variant matches the specified version when loading an lkenv object from 167 // disk. 168 variant envVariant 169 } 170 171 // cToGoString convert string in passed byte array into string type 172 // if string in byte array is not terminated, empty string is returned 173 func cToGoString(c []byte) string { 174 if end := bytes.IndexByte(c, 0); end >= 0 { 175 return string(c[:end]) 176 } 177 // no trailing \0 - return "" 178 return "" 179 } 180 181 // copyString copy passed string into byte array 182 // make sure string is terminated 183 // if string does not fit into byte array, it will be concatenated 184 func copyString(b []byte, s string) { 185 sl := len(s) 186 bs := len(b) 187 if bs > sl { 188 copy(b[:], s) 189 b[sl] = 0 190 } else { 191 copy(b[:bs-1], s) 192 b[bs-1] = 0 193 } 194 } 195 196 // NewEnv creates a new lkenv object referencing the primary bootloader 197 // environment file at path with the specified version. If the specified filed 198 // is expected to be a valid lkenv object, then the object should be loaded with 199 // the Load() method, otherwise the lkenv object can be manipulated in memory 200 // and later written to disk with Save(). 201 func NewEnv(path, backupPath string, version Version) *Env { 202 if backupPath == "" { 203 // legacy behavior is for the backup file to be the same name/dir, but 204 // with "bak" appended to it 205 backupPath = path + "bak" 206 } 207 e := &Env{ 208 path: path, 209 pathbak: backupPath, 210 version: version, 211 } 212 213 switch version { 214 case V1: 215 e.variant = newV1() 216 case V2Recovery: 217 e.variant = newV2Recovery() 218 case V2Run: 219 e.variant = newV2Run() 220 } 221 return e 222 } 223 224 // Load will load the lk bootloader environment from it's configured primary 225 // environment file, and if that fails it will fallback to trying the backup 226 // environment file. 227 func (l *Env) Load() error { 228 err := l.LoadEnv(l.path) 229 if err != nil { 230 logger.Noticef("cannot load primary bootloader environment: %v\n", err) 231 logger.Noticef("attempting to load backup bootloader environment\n") 232 return l.LoadEnv(l.pathbak) 233 } 234 return nil 235 } 236 237 type compatErrNotExist struct { 238 err error 239 } 240 241 func (e compatErrNotExist) Error() string { 242 return e.err.Error() 243 } 244 245 func (e compatErrNotExist) Unwrap() error { 246 // for go 1.9 (and 1.10) xerrors compatibility, we check if os.PathError 247 // implements Unwrap(), and if not return os.ErrNotExist directly 248 if _, ok := e.err.(interface { 249 Unwrap() error 250 }); !ok { 251 return os.ErrNotExist 252 } 253 return e.err 254 } 255 256 // LoadEnv loads the lk bootloader environment from the specified file. The 257 // bootloader environment in the referenced file must be of the same version 258 // that the Env object was created with using NewEnv. 259 // The returned error may wrap os.ErrNotExist, so instead of using 260 // os.IsNotExist, callers should use xerrors.Is(err,os.ErrNotExist) instead. 261 func (l *Env) LoadEnv(path string) error { 262 f, err := os.Open(path) 263 if err != nil { 264 // TODO: when we drop support for Go 1.9, this code can go away, in Go 265 // 1.9 *os.PathError does not implement Unwrap(), and so callers 266 // that try to call xerrors.Is(err,os.ErrNotExist) will fail, so 267 // instead we do our own wrapping first such that when Unwrap() is 268 // called by xerrors.Is() it will see os.ErrNotExist directly when 269 // compiled with a version of Go that does not implement Unwrap() 270 // on os.PathError 271 if os.IsNotExist(err) { 272 err = compatErrNotExist{err: err} 273 } 274 fmtStr := "cannot open LK env file: %w" 275 return xerrors.Errorf(fmtStr, err) 276 } 277 278 if err := binary.Read(f, binary.LittleEndian, l.variant); err != nil { 279 return fmt.Errorf("cannot read LK env from file: %v", err) 280 } 281 282 // validate the version and signatures 283 v := l.variant.currentVersion() 284 s := l.variant.currentSignature() 285 expV := l.version.Number() 286 expS := l.version.Signature() 287 288 if expV != v { 289 return fmt.Errorf("cannot validate %s: expected version 0x%X, got 0x%X", path, expV, v) 290 } 291 292 if expS != s { 293 return fmt.Errorf("cannot validate %s: expected signature 0x%X, got 0x%X", path, expS, s) 294 } 295 296 // independently calculate crc32 to validate structure 297 w := bytes.NewBuffer(nil) 298 ss := binary.Size(l.variant) 299 w.Grow(ss) 300 if err := binary.Write(w, binary.LittleEndian, l.variant); err != nil { 301 return fmt.Errorf("cannot write LK env to buffer for validation: %v", err) 302 } 303 304 crc := crc32.ChecksumIEEE(w.Bytes()[:ss-4]) // size of crc32 itself at the end of the structure 305 if crc != l.variant.currentCrc32() { 306 return fmt.Errorf("cannot validate %s: expected checksum 0x%X, got 0x%X", path, crc, l.variant.currentCrc32()) 307 } 308 logger.Debugf("validated crc32 as 0x%X for lkenv loaded from file %s", l.variant.currentCrc32(), path) 309 310 return nil 311 } 312 313 // Save saves the lk bootloader environment to the configured primary 314 // environment file, and if the backup environment file exists, the backup too. 315 // Save will also update the CRC32 of the environment when writing the file(s). 316 func (l *Env) Save() error { 317 buf := bytes.NewBuffer(nil) 318 ss := binary.Size(l.variant) 319 buf.Grow(ss) 320 if err := binary.Write(buf, binary.LittleEndian, l.variant); err != nil { 321 return fmt.Errorf("cannot write LK env to buffer for saving: %v", err) 322 } 323 324 // calculate crc32 325 newCrc32 := crc32.ChecksumIEEE(buf.Bytes()[:ss-4]) 326 logger.Debugf("calculated lk bootloader environment crc32 as 0x%X to save", newCrc32) 327 // note for efficiency's sake to avoid re-writing the whole structure, we 328 // re-write _just_ the crc32 to w as little-endian 329 buf.Truncate(ss - 4) 330 binary.Write(buf, binary.LittleEndian, &newCrc32) 331 332 err := l.saveEnv(l.path, buf) 333 if err != nil { 334 logger.Noticef("failed to save primary bootloader environment: %v", err) 335 } 336 // if there is backup environment file save to it as well 337 if osutil.FileExists(l.pathbak) { 338 // TODO: if the primary succeeds but saving to the backup fails, we 339 // don't return non-nil error here, should we? 340 if err := l.saveEnv(l.pathbak, buf); err != nil { 341 logger.Noticef("failed to save backup environment: %v", err) 342 } 343 } 344 return err 345 } 346 347 func (l *Env) saveEnv(path string, buf *bytes.Buffer) error { 348 f, err := os.OpenFile(path, os.O_WRONLY, 0660) 349 if err != nil { 350 return fmt.Errorf("cannot open LK env file for env storing: %v", err) 351 } 352 defer f.Close() 353 354 if _, err := f.Write(buf.Bytes()); err != nil { 355 return fmt.Errorf("cannot write LK env buf to LK env file: %v", err) 356 } 357 if err := f.Sync(); err != nil { 358 return fmt.Errorf("cannot sync LK env file: %v", err) 359 } 360 return nil 361 } 362 363 // Get returns the value of the key from the environment. If the key specified 364 // is not supported for the environment, the empty string is returned. 365 func (l *Env) Get(key string) string { 366 return l.variant.get(key) 367 } 368 369 // Set assigns the value to the key in the environment. If the key specified is 370 // not supported for the environment, nothing happens. 371 func (l *Env) Set(key, value string) { 372 l.variant.set(key, value) 373 } 374 375 // InitializeBootPartitions sets the boot image partition label names. 376 // This function should not be used at run time! 377 // It should be used only at image build time, if partition labels are not 378 // pre-filled by gadget built, currently it is only used inside snapd for tests. 379 func (l *Env) InitializeBootPartitions(bootPartLabels ...string) error { 380 var matr bootimgMatrixGeneric 381 var err error 382 // calculate the min/max limits for bootPartLabels 383 var min, max int 384 switch l.version { 385 case V1, V2Run: 386 min = 2 387 max = 2 388 matr, err = l.variant.bootImgKernelMatrix() 389 case V2Recovery: 390 min = 1 391 max = SNAP_RECOVERY_BOOTIMG_PART_NUM 392 matr, err = l.variant.bootImgRecoverySystemMatrix() 393 } 394 if err != nil { 395 return err 396 } 397 398 return matr.initializeBootPartitions(bootPartLabels, min, max) 399 } 400 401 // FindFreeKernelBootPartition finds a free boot image partition to be used for 402 // a new kernel revision. It ignores the currently installed boot image 403 // partition used for the active kernel 404 func (l *Env) FindFreeKernelBootPartition(kernel string) (string, error) { 405 matr, err := l.variant.bootImgKernelMatrix() 406 if err != nil { 407 return "", err 408 } 409 410 // the reserved boot image partition value is just the current snap_kernel 411 // if it is set (it could be unset at image build time where the lkenv is 412 // unset and has no kernel revision values set for the boot image partitions) 413 installedKernels := []string{} 414 if installedKernel := l.variant.get("snap_kernel"); installedKernel != "" { 415 installedKernels = []string{installedKernel} 416 } 417 return matr.findFreeBootPartition(installedKernels, kernel) 418 } 419 420 // GetKernelBootPartition returns the first found boot image partition label 421 // that contains a reference to the given kernel revision. If the revision was 422 // not found, a non-nil error is returned. 423 func (l *Env) GetKernelBootPartition(kernel string) (string, error) { 424 matr, err := l.variant.bootImgKernelMatrix() 425 if err != nil { 426 return "", err 427 } 428 429 bootPart, err := matr.getBootPartWithValue(kernel) 430 if err != nil { 431 return "", fmt.Errorf("cannot find kernel %q: %v", kernel, err) 432 } 433 return bootPart, nil 434 } 435 436 // SetBootPartitionKernel sets the kernel revision reference for the provided 437 // boot image partition label. It returns a non-nil err if the provided boot 438 // image partition label was not found. 439 func (l *Env) SetBootPartitionKernel(bootpart, kernel string) error { 440 matr, err := l.variant.bootImgKernelMatrix() 441 if err != nil { 442 return err 443 } 444 445 return matr.setBootPart(bootpart, kernel) 446 } 447 448 // RemoveKernelFromBootPartition removes from the boot image matrix the 449 // first found boot image partition that contains a reference to the given 450 // kernel revision. If the referenced kernel revision was not found, a non-nil 451 // err is returned, otherwise the reference is removed and nil is returned. 452 func (l *Env) RemoveKernelFromBootPartition(kernel string) error { 453 matr, err := l.variant.bootImgKernelMatrix() 454 if err != nil { 455 return err 456 } 457 458 return matr.dropBootPartValue(kernel) 459 } 460 461 // FindFreeRecoverySystemBootPartition finds a free recovery system boot image 462 // partition to be used for the recovery kernel from the recovery system. It 463 // only considers boot image partitions that are currently not set to a recovery 464 // system to be free. 465 func (l *Env) FindFreeRecoverySystemBootPartition(recoverySystem string) (string, error) { 466 matr, err := l.variant.bootImgRecoverySystemMatrix() 467 if err != nil { 468 return "", err 469 } 470 471 // when we create a new recovery system partition, we set all current 472 // recovery systems as reserved, so first get that list 473 currentRecoverySystems := matr.assignedBootPartValues() 474 return matr.findFreeBootPartition(currentRecoverySystems, recoverySystem) 475 } 476 477 // SetBootPartitionRecoverySystem sets the recovery system reference for the 478 // provided boot image partition. It returns a non-nil err if the provided boot 479 // partition reference was not found. 480 func (l *Env) SetBootPartitionRecoverySystem(bootpart, recoverySystem string) error { 481 matr, err := l.variant.bootImgRecoverySystemMatrix() 482 if err != nil { 483 return err 484 } 485 486 return matr.setBootPart(bootpart, recoverySystem) 487 } 488 489 // GetRecoverySystemBootPartition returns the first found boot image partition 490 // label that contains a reference to the given recovery system. If the recovery 491 // system was not found, a non-nil error is returned. 492 func (l *Env) GetRecoverySystemBootPartition(recoverySystem string) (string, error) { 493 matr, err := l.variant.bootImgRecoverySystemMatrix() 494 if err != nil { 495 return "", err 496 } 497 498 bootPart, err := matr.getBootPartWithValue(recoverySystem) 499 if err != nil { 500 return "", fmt.Errorf("cannot find recovery system %q: %v", recoverySystem, err) 501 } 502 return bootPart, nil 503 } 504 505 // RemoveRecoverySystemFromBootPartition removes from the boot image matrix the 506 // first found boot partition that contains a reference to the given recovery 507 // system. If the referenced recovery system was not found, a non-nil err is 508 // returned, otherwise the reference is removed and nil is returned. 509 func (l *Env) RemoveRecoverySystemFromBootPartition(recoverySystem string) error { 510 matr, err := l.variant.bootImgRecoverySystemMatrix() 511 if err != nil { 512 return err 513 } 514 515 return matr.dropBootPartValue(recoverySystem) 516 } 517 518 // GetBootImageName return expected boot image file name in kernel snap. If 519 // unset, it will return the default boot.img name. 520 func (l *Env) GetBootImageName() string { 521 fn := l.Get("bootimg_file_name") 522 if fn != "" { 523 return fn 524 } 525 return BOOTIMG_DEFAULT_NAME 526 } 527 528 // common matrix helper methods which operate on the boot image matrix, which is 529 // a mapping of boot image partition label to either a kernel revision or a 530 // recovery system label. 531 532 // bootimgMatrixGeneric is a generic slice version of the above two matrix types 533 // which are both statically sized arrays, and thus not able to be used 534 // interchangeably while the slice is. 535 type bootimgMatrixGeneric [][2][SNAP_FILE_NAME_MAX_LEN]byte 536 537 // initializeBootPartitions is a test helper method to set all the boot image 538 // partition labels for a lkenv object, normally this is done by the gadget at 539 // image build time and not done by snapd, but we do this in tests. 540 // The min and max arguments are for size checking of the provided array of 541 // bootPartLabels 542 func (matr bootimgMatrixGeneric) initializeBootPartitions(bootPartLabels []string, min, max int) error { 543 numBootPartLabels := len(bootPartLabels) 544 545 if numBootPartLabels < min || numBootPartLabels > max { 546 return fmt.Errorf("invalid number of boot image partitions, expected %d got %d", len(matr), numBootPartLabels) 547 } 548 for x, label := range bootPartLabels { 549 copyString(matr[x][MATRIX_ROW_PARTITION][:], label) 550 } 551 return nil 552 } 553 554 // dropBootPartValue will remove the specified bootPartValue from the boot image 555 // matrix - it _only_ deletes the value, not the boot image partition label 556 // itself, , as the boot image partition labels are static for the lifetime of a 557 // device and should never be changed (as those values correspond to physical 558 // names of the formatted partitions and we don't yet support repartitioning of 559 // any kind). 560 func (matr bootimgMatrixGeneric) dropBootPartValue(bootPartValue string) error { 561 for x := range matr { 562 if "" != cToGoString(matr[x][MATRIX_ROW_PARTITION][:]) { 563 if bootPartValue == cToGoString(matr[x][MATRIX_ROW_VALUE][:]) { 564 // clear the string by setting the first element to 0 or NUL 565 matr[x][MATRIX_ROW_VALUE][0] = 0 566 return nil 567 } 568 } 569 } 570 571 return fmt.Errorf("cannot find %q in boot image partitions", bootPartValue) 572 } 573 574 // setBootPart associates the specified boot image partition label to the 575 // specified value. 576 func (matr bootimgMatrixGeneric) setBootPart(bootpart, bootPartValue string) error { 577 for x := range matr { 578 if bootpart == cToGoString(matr[x][MATRIX_ROW_PARTITION][:]) { 579 copyString(matr[x][MATRIX_ROW_VALUE][:], bootPartValue) 580 return nil 581 } 582 } 583 584 return fmt.Errorf("cannot find boot image partition %s", bootpart) 585 } 586 587 // findFreeBootPartition will return a boot image partition that can be 588 // used for a new value, specifically skipping the reserved values. It may 589 // return either a boot image partition that does not contain any value or 590 // a boot image partition that already contains the specified value. The 591 // reserved argument is typically used for already installed values, such as the 592 // currently installed kernel snap revision, so that a new try kernel snap does 593 // not overwrite the existing installed kernel snap. 594 func (matr bootimgMatrixGeneric) findFreeBootPartition(reserved []string, newValue string) (string, error) { 595 for x := range matr { 596 bootPartLabel := cToGoString(matr[x][MATRIX_ROW_PARTITION][:]) 597 // skip boot image partition labels that are unset, for example this may 598 // happen if a system only has 3 physical boot image partitions for 599 // recovery system kernels, but the same matrix structure has 10 slots 600 // and all 3 usable slots are in use by installed reserved recovery 601 // systems. 602 if bootPartLabel == "" { 603 continue 604 } 605 606 val := cToGoString(matr[x][MATRIX_ROW_VALUE][:]) 607 608 // if the value is exactly the same, as requested return it, this needs 609 // to be handled before checking the reserved values since we may 610 // sometimes need to find a "free" boot partition for the specific 611 // kernel revision that is already installed, thus it will show up in 612 // the reserved list, but it will also be newValue 613 // this case happens in practice during seeding of kernels on uc16/uc18, 614 // where we already extracted the kernel at image build time and we will 615 // go to extract the kernel again during seeding 616 if val == newValue { 617 return bootPartLabel, nil 618 } 619 620 // if this value was reserved, skip it 621 if strutil.ListContains(reserved, val) { 622 continue 623 } 624 625 // otherwise consider it to be free, even if it was set to something 626 // else - this is because callers should be using reserved to prevent 627 // overwriting the wrong boot image partition value 628 return bootPartLabel, nil 629 } 630 631 return "", fmt.Errorf("cannot find free boot image partition") 632 } 633 634 // assignedBootPartValues returns all boot image partitions values that are set. 635 func (matr bootimgMatrixGeneric) assignedBootPartValues() []string { 636 bootPartValues := make([]string, 0, len(matr)) 637 for x := range matr { 638 bootPartLabel := cToGoString(matr[x][MATRIX_ROW_PARTITION][:]) 639 if bootPartLabel != "" { 640 // now check the value 641 bootPartValue := cToGoString(matr[x][MATRIX_ROW_VALUE][:]) 642 if bootPartValue != "" { 643 bootPartValues = append(bootPartValues, bootPartValue) 644 } 645 } 646 } 647 648 return bootPartValues 649 } 650 651 // getBootPartWithValue returns the boot image partition label for the specified value. 652 // If the boot image partition label does not exist in the matrix, an error will 653 // be returned. 654 func (matr bootimgMatrixGeneric) getBootPartWithValue(value string) (string, error) { 655 for x := range matr { 656 if value == cToGoString(matr[x][MATRIX_ROW_VALUE][:]) { 657 return cToGoString(matr[x][MATRIX_ROW_PARTITION][:]), nil 658 } 659 } 660 661 return "", fmt.Errorf("no boot image partition has value %q", value) 662 }