mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-04-27 11:21:44 +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 {
|
||||
return err
|
||||
}
|
||||
loopDevice, err := loop.Loop(img, e.config)
|
||||
loopDevice, err := loop.GetLoopManager(e.config).Loop(img)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -265,7 +265,7 @@ func (e Elemental) UnmountImage(img *v1.Image) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = loop.Unloop(img.LoopDevice, e.config)
|
||||
err = loop.GetLoopManager(e.config).Unloop(img.LoopDevice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -2,87 +2,106 @@ package loop
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
"golang.org/x/sys/unix"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||||
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,
|
||||
// including success (errno 0) so we need to check its value to know if its an actual error or not
|
||||
func errnoIsErr(err error) error {
|
||||
var (
|
||||
instance *LoopManager
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loop will setup a /dev/loopX device linked to the image file by using syscalls directly to set it
|
||||
func Loop(img *v1.Image, cfg *config.Config) (loopDevice string, err error) {
|
||||
log := cfg.Logger
|
||||
func (lm *LoopManager) Loop(img *v1.Image) (loopDevice string, err error) {
|
||||
lm.mu.Lock()
|
||||
defer lm.mu.Unlock()
|
||||
|
||||
log := lm.cfg.Logger
|
||||
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 {
|
||||
log.Error("failed to open /dev/loop-control")
|
||||
return loopDevice, err
|
||||
}
|
||||
|
||||
defer fd.Close()
|
||||
|
||||
log.Debugf("Getting free loop device")
|
||||
loopInt, _, err := cfg.Syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), unix.LOOP_CTL_GET_FREE, 0)
|
||||
if errnoIsErr(err) != nil {
|
||||
loopInt, _, err := lm.cfg.Syscall.Syscall(syscall.SYS_IOCTL, fd.Fd(), unix.LOOP_CTL_GET_FREE, 0)
|
||||
if lm.errnoIsErr(err) != nil {
|
||||
log.Error("failed to get loop device")
|
||||
return loopDevice, err
|
||||
}
|
||||
|
||||
loopDevice = fmt.Sprintf("/dev/loop%d", loopInt)
|
||||
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 {
|
||||
log.Error("failed to open loop device")
|
||||
return loopDevice, err
|
||||
}
|
||||
defer loopFile.Close()
|
||||
|
||||
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 {
|
||||
log.Error("failed to open image file")
|
||||
return loopDevice, err
|
||||
}
|
||||
defer loopFile.Close()
|
||||
defer imageFile.Close()
|
||||
|
||||
log.Debugf("Setting loop device")
|
||||
_, _, err = cfg.Syscall.Syscall(
|
||||
_, _, err = lm.cfg.Syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
loopFile.Fd(),
|
||||
unix.LOOP_SET_FD,
|
||||
imageFile.Fd(),
|
||||
)
|
||||
if errnoIsErr(err) != nil {
|
||||
if lm.errnoIsErr(err) != nil {
|
||||
log.Error("failed to set loop device")
|
||||
return loopDevice, err
|
||||
}
|
||||
|
||||
// Force kernel to scan partition table on loop device
|
||||
status := &unix.LoopInfo64{
|
||||
Flags: unix.LO_FLAGS_PARTSCAN,
|
||||
}
|
||||
// Dont set read only flag
|
||||
status.Flags &= ^uint32(unix.LO_FLAGS_READ_ONLY)
|
||||
|
||||
log.Debugf("Setting loop flags")
|
||||
_, _, err = cfg.Syscall.Syscall(
|
||||
_, _, err = lm.cfg.Syscall.Syscall(
|
||||
syscall.SYS_IOCTL,
|
||||
loopFile.Fd(),
|
||||
unix.LOOP_SET_STATUS64,
|
||||
uintptr(unsafe.Pointer(status)),
|
||||
)
|
||||
|
||||
if errnoIsErr(err) != nil {
|
||||
if lm.errnoIsErr(err) != nil {
|
||||
log.Error("failed to set loop device status")
|
||||
return loopDevice, err
|
||||
}
|
||||
@ -90,20 +109,22 @@ func Loop(img *v1.Image, cfg *config.Config) (loopDevice string, err error) {
|
||||
return loopDevice, nil
|
||||
}
|
||||
|
||||
// Unloop will clear a loop device and free the underlying image linked to it
|
||||
func Unloop(loopDevice string, cfg *config.Config) error {
|
||||
log := cfg.Logger
|
||||
func (lm *LoopManager) Unloop(loopDevice string) error {
|
||||
lm.mu.Lock()
|
||||
defer lm.mu.Unlock()
|
||||
|
||||
log := lm.cfg.Logger
|
||||
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 {
|
||||
log.Error("failed to set open loop device")
|
||||
return err
|
||||
}
|
||||
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")
|
||||
return err
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user