2019-10-25 23:45:41 +03:00
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
2019-07-31 17:16:10 +00:00
2019-11-01 04:55:58 +00:00
package pkg
2019-07-31 17:16:10 +00:00
import (
2019-10-16 04:50:06 +00:00
"fmt"
2019-07-31 17:16:10 +00:00
"io"
2020-03-10 07:52:10 -07:00
"log"
2019-07-31 17:16:10 +00:00
"os"
"path/filepath"
"unsafe"
2019-09-12 14:32:42 +00:00
"golang.org/x/sys/unix"
2020-02-18 20:07:01 -08:00
"github.com/talos-systems/go-procfs/procfs"
2019-11-01 04:55:58 +00:00
"github.com/talos-systems/talos/cmd/installer/pkg/bootloader/syslinux"
"github.com/talos-systems/talos/cmd/installer/pkg/manifest"
2019-07-31 17:16:10 +00:00
"github.com/talos-systems/talos/internal/pkg/mount"
"github.com/talos-systems/talos/internal/pkg/mount/manager"
"github.com/talos-systems/talos/internal/pkg/mount/manager/owned"
2019-11-05 23:13:50 +00:00
"github.com/talos-systems/talos/internal/pkg/runtime"
2020-03-10 07:52:10 -07:00
"github.com/talos-systems/talos/pkg/blockdevice"
"github.com/talos-systems/talos/pkg/blockdevice/probe"
2020-04-21 15:07:31 -07:00
"github.com/talos-systems/talos/pkg/blockdevice/table"
"github.com/talos-systems/talos/pkg/blockdevice/util"
2019-09-30 14:30:11 -07:00
"github.com/talos-systems/talos/pkg/config/machine"
2019-08-01 23:01:31 +00:00
"github.com/talos-systems/talos/pkg/constants"
2019-07-31 17:16:10 +00:00
)
// Installer represents the installer logic. It serves as the entrypoint to all
// installation methods.
type Installer struct {
2020-02-18 20:07:01 -08:00
cmdline * procfs . Cmdline
2019-09-30 14:30:11 -07:00
install machine . Install
2019-07-31 17:16:10 +00:00
manifest * manifest . Manifest
2020-03-10 07:52:10 -07:00
Current string
Next string
bootPartitionFound bool
2019-07-31 17:16:10 +00:00
}
// NewInstaller initializes and returns an Installer.
2020-03-10 07:52:10 -07:00
//
// nolint: gocyclo
func NewInstaller ( cmdline * procfs . Cmdline , sequence runtime . Sequence , install machine . Install ) ( i * Installer , err error ) {
2019-08-15 15:21:55 +00:00
i = & Installer {
2019-07-31 17:16:10 +00:00
cmdline : cmdline ,
2019-09-30 14:30:11 -07:00
install : install ,
2019-07-31 17:16:10 +00:00
}
2020-03-10 07:52:10 -07:00
var dev * probe . ProbedBlockDevice
if dev , err = probe . GetDevWithFileSystemLabel ( constants . BootPartitionLabel ) ; err != nil {
i . bootPartitionFound = false
log . Printf ( "WARNING: failed to find %s: %v" , constants . BootPartitionLabel , err )
} else {
i . bootPartitionFound = true
}
if sequence == runtime . Upgrade && i . bootPartitionFound {
if err = os . MkdirAll ( "/boot" , 0777 ) ; err != nil {
return nil , err
}
if err = unix . Mount ( dev . Path , "/boot" , dev . SuperBlock . Type ( ) , 0 , "" ) ; err != nil {
return nil , fmt . Errorf ( "failed to mount /boot: %w" , err )
}
2020-03-26 11:04:55 -07:00
}
2020-03-10 07:52:10 -07:00
2020-03-26 11:04:55 -07:00
i . Current , i . Next , err = syslinux . Labels ( )
if err != nil {
return nil , err
}
2020-03-10 07:52:10 -07:00
2020-03-26 11:04:55 -07:00
label := i . Current
2020-03-10 07:52:10 -07:00
2020-03-26 11:04:55 -07:00
if sequence == runtime . Upgrade && i . bootPartitionFound {
label = i . Next
}
i . manifest , err = manifest . NewManifest ( label , sequence , install )
if err != nil {
return nil , fmt . Errorf ( "failed to create installation manifest: %w" , err )
2019-08-15 15:21:55 +00:00
}
2019-07-31 17:16:10 +00:00
2019-08-15 15:21:55 +00:00
return i , nil
2019-07-31 17:16:10 +00:00
}
// Install fetches the necessary data locations and copies or extracts
// to the target locations.
2020-03-10 07:52:10 -07:00
//
2019-07-31 17:16:10 +00:00
// nolint: gocyclo
2019-11-05 23:13:50 +00:00
func ( i * Installer ) Install ( sequence runtime . Sequence ) ( err error ) {
2020-03-10 07:52:10 -07:00
if sequence != runtime . Upgrade || ! i . bootPartitionFound {
if i . install . Zero ( ) {
if err = zero ( i . manifest ) ; err != nil {
return fmt . Errorf ( "failed to wipe device(s): %w" , err )
}
2019-07-31 17:16:10 +00:00
}
2020-03-10 07:52:10 -07:00
// Partition and format the block device(s).
if err = i . manifest . ExecuteManifest ( ) ; err != nil {
return err
}
2019-07-31 17:16:10 +00:00
2020-03-10 07:52:10 -07:00
// Mount the partitions.
2019-07-31 17:16:10 +00:00
2020-03-10 07:52:10 -07:00
mountpoints := mount . NewMountPoints ( )
// look for mountpoints across all target devices
for dev := range i . manifest . Targets {
var mp * mount . Points
2019-07-31 17:16:10 +00:00
2020-04-15 10:46:45 -07:00
mp , err = owned . MountPointsForDevice ( dev )
2020-03-10 07:52:10 -07:00
if err != nil {
return err
}
2019-10-10 01:00:42 +03:00
2020-03-10 07:52:10 -07:00
iter := mp . Iter ( )
for iter . Next ( ) {
mountpoints . Set ( iter . Key ( ) , iter . Value ( ) )
}
2019-07-31 17:16:10 +00:00
}
2019-08-08 00:16:43 -05:00
2020-03-10 07:52:10 -07:00
m := manager . NewManager ( mountpoints )
if err = m . MountAll ( ) ; err != nil {
return err
2019-07-31 17:16:10 +00:00
}
2020-03-10 07:52:10 -07:00
// nolint: errcheck
defer m . UnmountAll ( )
2019-07-31 17:16:10 +00:00
}
2020-04-20 19:47:32 -07:00
if sequence == runtime . Upgrade && i . bootPartitionFound && i . install . Force ( ) {
2020-03-10 07:52:10 -07:00
for dev , targets := range i . manifest . Targets {
var bd * blockdevice . BlockDevice
if bd , err = blockdevice . Open ( dev ) ; err != nil {
return err
}
// nolint: errcheck
defer bd . Close ( )
2020-04-21 15:07:31 -07:00
var pt table . PartitionTable
pt , err = bd . PartitionTable ( true )
if err != nil {
return err
}
2020-03-10 07:52:10 -07:00
for _ , target := range targets {
2020-04-21 15:07:31 -07:00
for _ , part := range pt . Partitions ( ) {
switch target . Label {
case constants . BootPartitionLabel , constants . EphemeralPartitionLabel :
target . PartitionName = util . PartPath ( target . Device , int ( part . No ( ) ) )
}
}
2020-03-10 07:52:10 -07:00
if target . Label == constants . BootPartitionLabel {
continue
}
if err = target . Format ( ) ; err != nil {
return fmt . Errorf ( "failed to format device: %w" , err )
}
}
}
}
2019-08-08 00:16:43 -05:00
2019-07-31 17:16:10 +00:00
// Install the assets.
for _ , targets := range i . manifest . Targets {
for _ , target := range targets {
switch target . Label {
case constants . BootPartitionLabel :
if err = syslinux . Prepare ( target . Device ) ; err != nil {
return err
}
2019-08-15 12:12:22 +00:00
case constants . EphemeralPartitionLabel :
2019-07-31 17:16:10 +00:00
continue
}
// Handle the download and extraction of assets.
if err = target . Save ( ) ; err != nil {
return err
}
}
}
// Install the bootloader.
2019-09-30 14:30:11 -07:00
if ! i . install . WithBootloader ( ) {
2019-07-31 17:16:10 +00:00
return nil
}
2020-03-10 07:52:10 -07:00
if sequence != runtime . Upgrade || ! i . bootPartitionFound {
i . cmdline . Append ( "initrd" , filepath . Join ( "/" , i . Current , constants . InitramfsAsset ) )
syslinuxcfg := & syslinux . Cfg {
Default : i . Current ,
Labels : [ ] * syslinux . Label {
{
Root : i . Current ,
Initrd : filepath . Join ( "/" , i . Current , constants . InitramfsAsset ) ,
Kernel : filepath . Join ( "/" , i . Current , constants . KernelAsset ) ,
Append : i . cmdline . String ( ) ,
} ,
2019-07-31 17:16:10 +00:00
} ,
2020-03-10 07:52:10 -07:00
}
2019-07-31 17:16:10 +00:00
2020-03-26 11:04:55 -07:00
if err = syslinux . Install ( "" , syslinuxcfg , sequence , i . bootPartitionFound ) ; err != nil {
2020-03-10 07:52:10 -07:00
return err
}
} else {
i . cmdline . Append ( "initrd" , filepath . Join ( "/" , i . Next , constants . InitramfsAsset ) )
syslinuxcfg := & syslinux . Cfg {
2020-03-26 11:04:55 -07:00
Default : i . Next ,
2020-03-10 07:52:10 -07:00
Labels : [ ] * syslinux . Label {
{
Root : i . Next ,
Initrd : filepath . Join ( "/" , i . Next , constants . InitramfsAsset ) ,
Kernel : filepath . Join ( "/" , i . Next , constants . KernelAsset ) ,
Append : i . cmdline . String ( ) ,
} ,
2020-03-26 11:04:55 -07:00
{
Root : i . Current ,
Initrd : filepath . Join ( "/" , i . Current , constants . InitramfsAsset ) ,
Kernel : filepath . Join ( "/" , i . Current , constants . KernelAsset ) ,
Append : procfs . ProcCmdline ( ) . String ( ) ,
} ,
2020-03-10 07:52:10 -07:00
} ,
}
2019-07-31 17:16:10 +00:00
2020-03-26 11:04:55 -07:00
if err = syslinux . Install ( i . Current , syslinuxcfg , sequence , i . bootPartitionFound ) ; err != nil {
2020-03-10 07:52:10 -07:00
return err
}
}
2019-11-05 23:13:50 +00:00
2020-03-10 07:52:10 -07:00
return nil
2019-07-31 17:16:10 +00:00
}
2019-09-30 14:30:11 -07:00
func zero ( manifest * manifest . Manifest ) ( err error ) {
2019-07-31 17:16:10 +00:00
var zero * os . File
2019-10-10 01:00:42 +03:00
2019-07-31 17:16:10 +00:00
if zero , err = os . Open ( "/dev/zero" ) ; err != nil {
return err
}
2020-04-03 22:54:51 +03:00
defer zero . Close ( ) //nolint: errcheck
2019-07-31 17:16:10 +00:00
for dev := range manifest . Targets {
2020-04-03 22:54:51 +03:00
if err = func ( dev string ) error {
var f * os . File
2019-10-10 01:00:42 +03:00
2020-04-03 22:54:51 +03:00
if f , err = os . OpenFile ( dev , os . O_RDWR , os . ModeDevice ) ; err != nil {
return err
}
2019-10-10 01:00:42 +03:00
2020-04-03 22:54:51 +03:00
defer f . Close ( ) //nolint: errcheck
2019-10-10 01:00:42 +03:00
2020-04-03 22:54:51 +03:00
var size uint64
2019-10-10 01:00:42 +03:00
2020-04-03 22:54:51 +03:00
if _ , _ , ret := unix . Syscall ( unix . SYS_IOCTL , f . Fd ( ) , unix . BLKGETSIZE64 , uintptr ( unsafe . Pointer ( & size ) ) ) ; ret != 0 {
return fmt . Errorf ( "failed to got block device size: %v" , ret )
}
if _ , err = io . CopyN ( f , zero , int64 ( size ) ) ; err != nil {
return err
}
2019-07-31 17:16:10 +00:00
2020-04-03 22:54:51 +03:00
return f . Close ( )
} ( dev ) ; err != nil {
2019-07-31 17:16:10 +00:00
return err
}
}
2020-04-03 22:54:51 +03:00
return zero . Close ( )
2019-07-31 17:16:10 +00:00
}