mirror of
https://github.com/kairos-io/kairos-agent.git
synced 2025-04-28 11:36:48 +00:00
113 lines
3.0 KiB
Go
113 lines
3.0 KiB
Go
|
package loop
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"github.com/kairos-io/kairos-agent/v2/pkg/config"
|
||
|
"os"
|
||
|
"syscall"
|
||
|
"unsafe"
|
||
|
|
||
|
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 {
|
||
|
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
|
||
|
log.Debugf("Opening loop control device")
|
||
|
fd, err := 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 {
|
||
|
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)
|
||
|
if err != nil {
|
||
|
log.Error("failed to open loop device")
|
||
|
return loopDevice, err
|
||
|
}
|
||
|
log.Logger.Debug().Str("image", img.File).Msg("Opening img file")
|
||
|
imageFile, err := 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(
|
||
|
syscall.SYS_IOCTL,
|
||
|
loopFile.Fd(),
|
||
|
unix.LOOP_SET_FD,
|
||
|
imageFile.Fd(),
|
||
|
)
|
||
|
if 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(
|
||
|
syscall.SYS_IOCTL,
|
||
|
loopFile.Fd(),
|
||
|
unix.LOOP_SET_STATUS64,
|
||
|
uintptr(unsafe.Pointer(status)),
|
||
|
)
|
||
|
|
||
|
if errnoIsErr(err) != nil {
|
||
|
log.Error("failed to set loop device status")
|
||
|
return loopDevice, err
|
||
|
}
|
||
|
|
||
|
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
|
||
|
log.Logger.Debug().Str("device", loopDevice).Msg("Opening loop device")
|
||
|
fd, err := 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.Error("failed to set loop device status")
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|