mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-04-27 19:28:59 +00:00
Use a singleton loopManager with mutex locking
Signed-off-by: Itxaka <itxaka@kairos.io>
This commit is contained in:
parent
71e8e5b801
commit
6ad942294b
@ -236,7 +236,7 @@ func (e Elemental) MountImage(img *v1.Image, opts ...string) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
loopDevice, err := loop.Loop(img, e.config)
|
loopDevice, err := loop.GetLoopManager(e.config).Loop(img)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -265,7 +265,7 @@ func (e Elemental) UnmountImage(img *v1.Image) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = loop.Unloop(img.LoopDevice, e.config)
|
err = loop.GetLoopManager(e.config).Unloop(img.LoopDevice)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2,87 +2,106 @@ package loop
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
"golang.org/x/sys/unix"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||||
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
v1 "github.com/kairos-io/kairos-agent/v2/pkg/types/v1"
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// syscalls will return an errno type (which implements error) for all calls,
|
var (
|
||||||
// including success (errno 0) so we need to check its value to know if its an actual error or not
|
instance *LoopManager
|
||||||
func errnoIsErr(err error) error {
|
once sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
type LoopManager struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
cfg *config.Config
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLoopManager returns a singleton instance of LoopManager
|
||||||
|
// So we can safely call it from different places and it will work properly with the mutex locking
|
||||||
|
func GetLoopManager(cfg *config.Config) *LoopManager {
|
||||||
|
once.Do(func() {
|
||||||
|
instance = &LoopManager{
|
||||||
|
cfg: cfg,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
func (lm *LoopManager) errnoIsErr(err error) error {
|
||||||
if err != nil && err.(syscall.Errno) != 0 {
|
if err != nil && err.(syscall.Errno) != 0 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop will setup a /dev/loopX device linked to the image file by using syscalls directly to set it
|
func (lm *LoopManager) Loop(img *v1.Image) (loopDevice string, err error) {
|
||||||
func Loop(img *v1.Image, cfg *config.Config) (loopDevice string, err error) {
|
lm.mu.Lock()
|
||||||
log := cfg.Logger
|
defer lm.mu.Unlock()
|
||||||
|
|
||||||
|
log := lm.cfg.Logger
|
||||||
log.Debugf("Opening loop control device")
|
log.Debugf("Opening loop control device")
|
||||||
fd, err := cfg.Fs.OpenFile("/dev/loop-control", os.O_RDONLY, 0o644)
|
fd, err := lm.cfg.Fs.OpenFile("/dev/loop-control", os.O_RDONLY, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to open /dev/loop-control")
|
log.Error("failed to open /dev/loop-control")
|
||||||
return loopDevice, err
|
return loopDevice, err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
|
|
||||||
log.Debugf("Getting free loop device")
|
log.Debugf("Getting free loop device")
|
||||||
loopInt, _, err := cfg.Syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), unix.LOOP_CTL_GET_FREE, 0)
|
loopInt, _, err := lm.cfg.Syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), unix.LOOP_CTL_GET_FREE, 0)
|
||||||
if errnoIsErr(err) != nil {
|
if lm.errnoIsErr(err) != nil {
|
||||||
log.Error("failed to get loop device")
|
log.Error("failed to get loop device")
|
||||||
return loopDevice, err
|
return loopDevice, err
|
||||||
}
|
}
|
||||||
|
|
||||||
loopDevice = fmt.Sprintf("/dev/loop%d", loopInt)
|
loopDevice = fmt.Sprintf("/dev/loop%d", loopInt)
|
||||||
log.Logger.Debug().Str("device", loopDevice).Msg("Opening loop device")
|
log.Logger.Debug().Str("device", loopDevice).Msg("Opening loop device")
|
||||||
loopFile, err := cfg.Fs.OpenFile(loopDevice, os.O_RDWR, 0)
|
loopFile, err := lm.cfg.Fs.OpenFile(loopDevice, os.O_RDWR, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to open loop device")
|
log.Error("failed to open loop device")
|
||||||
return loopDevice, err
|
return loopDevice, err
|
||||||
}
|
}
|
||||||
|
defer loopFile.Close()
|
||||||
|
|
||||||
log.Logger.Debug().Str("image", img.File).Msg("Opening img file")
|
log.Logger.Debug().Str("image", img.File).Msg("Opening img file")
|
||||||
imageFile, err := cfg.Fs.OpenFile(img.File, os.O_RDWR, os.ModePerm)
|
imageFile, err := lm.cfg.Fs.OpenFile(img.File, os.O_RDWR, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to open image file")
|
log.Error("failed to open image file")
|
||||||
return loopDevice, err
|
return loopDevice, err
|
||||||
}
|
}
|
||||||
defer loopFile.Close()
|
|
||||||
defer imageFile.Close()
|
defer imageFile.Close()
|
||||||
|
|
||||||
log.Debugf("Setting loop device")
|
log.Debugf("Setting loop device")
|
||||||
_, _, err = cfg.Syscall.Syscall(
|
_, _, err = lm.cfg.Syscall.Syscall(
|
||||||
syscall.SYS_IOCTL,
|
syscall.SYS_IOCTL,
|
||||||
loopFile.Fd(),
|
loopFile.Fd(),
|
||||||
unix.LOOP_SET_FD,
|
unix.LOOP_SET_FD,
|
||||||
imageFile.Fd(),
|
imageFile.Fd(),
|
||||||
)
|
)
|
||||||
if errnoIsErr(err) != nil {
|
if lm.errnoIsErr(err) != nil {
|
||||||
log.Error("failed to set loop device")
|
log.Error("failed to set loop device")
|
||||||
return loopDevice, err
|
return loopDevice, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force kernel to scan partition table on loop device
|
|
||||||
status := &unix.LoopInfo64{
|
status := &unix.LoopInfo64{
|
||||||
Flags: unix.LO_FLAGS_PARTSCAN,
|
Flags: unix.LO_FLAGS_PARTSCAN,
|
||||||
}
|
}
|
||||||
// Dont set read only flag
|
|
||||||
status.Flags &= ^uint32(unix.LO_FLAGS_READ_ONLY)
|
status.Flags &= ^uint32(unix.LO_FLAGS_READ_ONLY)
|
||||||
|
|
||||||
log.Debugf("Setting loop flags")
|
log.Debugf("Setting loop flags")
|
||||||
_, _, err = cfg.Syscall.Syscall(
|
_, _, err = lm.cfg.Syscall.Syscall(
|
||||||
syscall.SYS_IOCTL,
|
syscall.SYS_IOCTL,
|
||||||
loopFile.Fd(),
|
loopFile.Fd(),
|
||||||
unix.LOOP_SET_STATUS64,
|
unix.LOOP_SET_STATUS64,
|
||||||
uintptr(unsafe.Pointer(status)),
|
uintptr(unsafe.Pointer(status)),
|
||||||
)
|
)
|
||||||
|
if lm.errnoIsErr(err) != nil {
|
||||||
if errnoIsErr(err) != nil {
|
|
||||||
log.Error("failed to set loop device status")
|
log.Error("failed to set loop device status")
|
||||||
return loopDevice, err
|
return loopDevice, err
|
||||||
}
|
}
|
||||||
@ -90,20 +109,22 @@ func Loop(img *v1.Image, cfg *config.Config) (loopDevice string, err error) {
|
|||||||
return loopDevice, nil
|
return loopDevice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unloop will clear a loop device and free the underlying image linked to it
|
func (lm *LoopManager) Unloop(loopDevice string) error {
|
||||||
func Unloop(loopDevice string, cfg *config.Config) error {
|
lm.mu.Lock()
|
||||||
log := cfg.Logger
|
defer lm.mu.Unlock()
|
||||||
|
|
||||||
|
log := lm.cfg.Logger
|
||||||
log.Logger.Debug().Str("device", loopDevice).Msg("Opening loop device")
|
log.Logger.Debug().Str("device", loopDevice).Msg("Opening loop device")
|
||||||
fd, err := cfg.Fs.OpenFile(loopDevice, os.O_RDONLY, 0o644)
|
fd, err := lm.cfg.Fs.OpenFile(loopDevice, os.O_RDONLY, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to set open loop device")
|
log.Error("failed to set open loop device")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer fd.Close()
|
defer fd.Close()
|
||||||
log.Debugf("Clearing loop device")
|
|
||||||
_, _, err = cfg.Syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), unix.LOOP_CLR_FD, 0)
|
|
||||||
|
|
||||||
if errnoIsErr(err) != nil {
|
log.Debugf("Clearing loop device")
|
||||||
|
_, _, err = lm.cfg.Syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), unix.LOOP_CLR_FD, 0)
|
||||||
|
if lm.errnoIsErr(err) != nil {
|
||||||
log.Error("failed to set loop device status")
|
log.Error("failed to set loop device status")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user