2023-02-01 21:33:44 +00:00
package mount
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"github.com/containerd/containerd/mount"
"github.com/deniswernert/go-fstab"
"github.com/kairos-io/kairos/pkg/utils"
"github.com/moby/sys/mountinfo"
)
func rootFSType ( s string ) string {
out , _ := utils . SH ( fmt . Sprintf ( "findmnt -rno FSTYPE %s" , s ) )
return out
}
func createIfNotExists ( path string ) error {
if _ , err := os . Stat ( path ) ; os . IsNotExist ( err ) {
return os . MkdirAll ( path , os . ModePerm )
}
return nil
}
func appendSlash ( path string ) string {
if ! strings . HasSuffix ( path , "/" ) {
return fmt . Sprintf ( "%s/" , path )
}
return path
}
// https://github.com/kairos-io/packages/blob/94aa3bef3d1330cb6c6905ae164f5004b6a58b8c/packages/system/dracut/immutable-rootfs/30cos-immutable-rootfs/cos-mount-layout.sh#L129
2023-02-06 14:28:22 +00:00
func baseOverlay ( overlay Overlay ) ( mountOperation , error ) {
2023-02-01 21:33:44 +00:00
if err := os . MkdirAll ( overlay . Base , 0700 ) ; err != nil {
return mountOperation { } , err
}
dat := strings . Split ( overlay . BackingBase , ":" )
if len ( dat ) != 2 {
return mountOperation { } , fmt . Errorf ( "invalid backing base. must be a tmpfs with a size or a block device. e.g. tmpfs:30%%, block:/dev/sda1. Input: %s" , overlay . BackingBase )
}
t := dat [ 0 ]
switch t {
case "tmpfs" :
2023-02-06 15:20:18 +00:00
tmpMount := mount . Mount { Type : "tmpfs" , Source : "tmpfs" , Options : [ ] string { fmt . Sprintf ( "size=%s" , dat [ 1 ] ) } }
2023-02-01 21:33:44 +00:00
err := mount . All ( [ ] mount . Mount { tmpMount } , overlay . Base )
2023-02-06 15:20:18 +00:00
tmpFstab := mountToStab ( tmpMount )
tmpFstab . File = overlay . BackingBase
2023-02-01 21:33:44 +00:00
return mountOperation {
MountOption : tmpMount ,
2023-02-06 15:20:18 +00:00
FstabEntry : * tmpFstab ,
2023-02-01 21:33:44 +00:00
Target : overlay . Base ,
} , err
case "block" :
blockMount := mount . Mount { Type : "auto" , Source : dat [ 1 ] }
err := mount . All ( [ ] mount . Mount { blockMount } , overlay . Base )
2023-02-06 15:20:18 +00:00
tmpFstab := mountToStab ( blockMount )
tmpFstab . File = overlay . BackingBase
tmpFstab . MntOps [ "default" ] = ""
2023-02-01 21:33:44 +00:00
return mountOperation {
MountOption : blockMount ,
2023-02-06 15:20:18 +00:00
FstabEntry : * tmpFstab ,
2023-02-01 21:33:44 +00:00
Target : overlay . Base ,
} , err
default :
return mountOperation { } , fmt . Errorf ( "invalid overlay backing base type" )
}
}
func mountToStab ( m mount . Mount ) * fstab . Mount {
opts := map [ string ] string { }
for _ , o := range m . Options {
if strings . Contains ( o , "=" ) {
dat := strings . Split ( o , "=" )
key := dat [ 0 ]
value := dat [ 1 ]
opts [ key ] = value
} else {
opts [ o ] = ""
}
}
return & fstab . Mount {
Spec : m . Source ,
VfsType : m . Type ,
MntOps : opts ,
Freq : 0 ,
PassNo : 0 ,
}
}
// https://github.com/kairos-io/packages/blob/94aa3bef3d1330cb6c6905ae164f5004b6a58b8c/packages/system/dracut/immutable-rootfs/30cos-immutable-rootfs/cos-mount-layout.sh#L183
2023-02-08 13:43:58 +00:00
func mountBind ( mountpoint , root , stateTarget string ) mountOperation {
2023-02-01 21:33:44 +00:00
mountpoint = strings . TrimLeft ( mountpoint , "/" ) // normalize, remove / upfront as we are going to re-use it in subdirs
rootMount := filepath . Join ( root , mountpoint )
bindMountPath := strings . ReplaceAll ( mountpoint , "/" , "-" )
stateDir := filepath . Join ( root , stateTarget , fmt . Sprintf ( "%s.bind" , bindMountPath ) )
2023-02-08 13:43:58 +00:00
tmpMount := mount . Mount {
Type : "overlay" ,
Source : stateDir ,
Options : [ ] string {
//"defaults",
"bind" ,
} ,
}
2023-02-01 21:33:44 +00:00
2023-02-08 13:43:58 +00:00
tmpFstab := mountToStab ( tmpMount )
tmpFstab . File = fmt . Sprintf ( "/%s" , mountpoint )
tmpFstab . Spec = strings . ReplaceAll ( tmpFstab . Spec , root , "" )
return mountOperation {
MountOption : tmpMount ,
FstabEntry : * tmpFstab ,
Target : rootMount ,
PrepareCallback : func ( ) error {
if err := createIfNotExists ( rootMount ) ; err != nil {
return err
}
if err := createIfNotExists ( stateDir ) ; err != nil {
return err
}
return syncState ( appendSlash ( rootMount ) , appendSlash ( stateDir ) )
} ,
2023-02-01 21:33:44 +00:00
}
}
func syncState ( src , dst string ) error {
return exec . Command ( "rsync" , "-aqAX" , src , dst ) . Run ( )
}
// https://github.com/kairos-io/packages/blob/94aa3bef3d1330cb6c6905ae164f5004b6a58b8c/packages/system/dracut/immutable-rootfs/30cos-immutable-rootfs/cos-mount-layout.sh#L145
func mountWithBaseOverlay ( mountpoint , root , base string ) ( mountOperation , error ) {
mountpoint = strings . TrimLeft ( mountpoint , "/" ) // normalize, remove / upfront as we are going to re-use it in subdirs
rootMount := filepath . Join ( root , mountpoint )
bindMountPath := strings . ReplaceAll ( mountpoint , "/" , "-" )
createIfNotExists ( rootMount )
if mounted , _ := mountinfo . Mounted ( rootMount ) ; ! mounted {
upperdir := filepath . Join ( base , bindMountPath , ".overlay" , "upper" )
workdir := filepath . Join ( base , bindMountPath , ".overlay" , "work" )
tmpMount := mount . Mount {
Type : "overlay" ,
Source : "overlay" ,
Options : [ ] string {
2023-02-08 07:45:52 +00:00
//"defaults",
2023-02-01 21:33:44 +00:00
fmt . Sprintf ( "lowerdir=%s" , rootMount ) ,
fmt . Sprintf ( "upperdir=%s" , upperdir ) ,
fmt . Sprintf ( "workdir=%s" , workdir ) ,
} ,
}
2023-02-06 15:20:18 +00:00
tmpFstab := mountToStab ( tmpMount )
tmpFstab . File = rootMount
2023-02-01 21:33:44 +00:00
// TODO: update fstab with x-systemd info
// https://github.com/kairos-io/packages/blob/94aa3bef3d1330cb6c6905ae164f5004b6a58b8c/packages/system/dracut/immutable-rootfs/30cos-immutable-rootfs/cos-mount-layout.sh#L170
return mountOperation {
MountOption : tmpMount ,
2023-02-06 15:20:18 +00:00
FstabEntry : * tmpFstab ,
2023-02-01 21:33:44 +00:00
Target : rootMount ,
PrepareCallback : func ( ) error {
// Make sure workdir and/or upper exists
os . MkdirAll ( upperdir , os . ModePerm )
os . MkdirAll ( workdir , os . ModePerm )
return nil
} ,
} , nil
}
return mountOperation { } , fmt . Errorf ( "already mounted" )
}