func setupLoop(backingFile string, param LoopParams) (*os.File, error) {
	for retry := 1; retry < 100; retry++ {
		num, err := getFreeLoopDev()
		if err != nil {
			return nil, err
		}
		loopDev := fmt.Sprintf(loopDevFormat, num)
		file, err := setupLoopDev(backingFile, loopDev, param)
		if err != nil {
			if strings.Contains(err.Error(), ebusyString) {
				time.Sleep(time.Millisecond * time.Duration(rand.Intn(retry*10)))
				continue
			}
			return nil, err
		}
		return file, nil
	}
	return nil, errors.New("timeout creating new loopback device")
}
setupLoop looks for (and possibly creates) a free loop device, and
then attaches backingFile to it.

When autoclear is true, caller should take care to close it when
done with the loop device. The loop device file handle keeps
loFlagsAutoclear in effect and we rely on it to clean up the loop
device. If caller closes the file handle after mounting the device,
kernel will clear the loop device after it is umounted. Otherwise
the loop device is cleared when the file handle is closed.

When autoclear is false, caller should be responsible to remove
the loop device when done with it.

Upon success, the file handle to the loop device is returned.
package mount
import (
	"fmt"
	"math/rand"
	"os"
	"strings"
	"syscall"
	"time"
	"unsafe"
	"github.com/pkg/errors"
	"golang.org/x/sys/unix"
)
const (
	loopControlPath = "/dev/loop-control"
	loopDevFormat   = "/dev/loop%d"
	ebusyString = "device or resource busy"
)
type LoopParams struct {
	Readonly bool
	Autoclear bool
	Direct bool
}
func ioctl(fd, req, args uintptr) (uintptr, uintptr, error) {
	r1, r2, errno := syscall.Syscall(syscall.SYS_IOCTL, fd, req, args)
	if errno != 0 {
		return 0, 0, errno
	}
	return r1, r2, nil
}
func getFreeLoopDev() (uint32, error) {
	ctrl, err := os.OpenFile(loopControlPath, os.O_RDWR, 0)
	if err != nil {
		return 0, errors.Errorf("could not open %v: %v", loopControlPath, err)
	}
	defer ctrl.Close()
	num, _, err := ioctl(ctrl.Fd(), unix.LOOP_CTL_GET_FREE, 0)
	if err != nil {
		return 0, errors.Wrap(err, "could not get free loop device")
	}
	return uint32(num), nil
}
func setupLoopDev(backingFile, loopDev string, param LoopParams) (_ *os.File, retErr error) {
	flags := os.O_RDWR
	if param.Readonly {
		flags = os.O_RDONLY
	}
	back, err := os.OpenFile(backingFile, flags, 0)
	if err != nil {
		return nil, errors.Wrapf(err, "could not open backing file: %s", backingFile)
	}
	defer back.Close()
	loop, err := os.OpenFile(loopDev, flags, 0)
	if err != nil {
		return nil, errors.Wrapf(err, "could not open loop device: %s", loopDev)
	}
	defer func() {
		if retErr != nil {
			loop.Close()
		}
	}()
	if _, _, err = ioctl(loop.Fd(), unix.LOOP_SET_FD, back.Fd()); err != nil {
		return nil, errors.Wrapf(err, "could not set loop fd for device: %s", loopDev)
	}
	info := unix.LoopInfo64{}
	copy(info.File_name[:], backingFile)
	if param.Readonly {
		info.Flags |= unix.LO_FLAGS_READ_ONLY
	}
	if param.Autoclear {
		info.Flags |= unix.LO_FLAGS_AUTOCLEAR
	}
	if param.Direct {
		info.Flags |= unix.LO_FLAGS_DIRECT_IO
	}
	_, _, err = ioctl(loop.Fd(), unix.LOOP_SET_STATUS64, uintptr(unsafe.Pointer(&info)))
	if err == nil {
		return loop, nil
	}
	if param.Direct {
		info.Flags &= ^(uint32(unix.LO_FLAGS_DIRECT_IO))
		_, _, err = ioctl(loop.Fd(), unix.LOOP_SET_STATUS64, uintptr(unsafe.Pointer(&info)))
		if err == nil {
			return loop, nil
		}
	}
	_, _, _ = ioctl(loop.Fd(), unix.LOOP_CLR_FD, 0)
	return nil, errors.Errorf("failed to set loop device info: %v", err)
}