func removeLoopDevice(ctx context.Context, device string) error {
	ctx, logger := pmemlog.WithName(ctx, "removeLoopDevice")
	args := []string{"-d", device}
	cmd := exec.Command(losetupPath, args...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		if _, err := os.Stat(device); os.IsNotExist(err) {
			return nil
		}
		logger.V(2).Info("Failed to remove loopback device", "device", device, "error", err, "stdout", string(out))
		return fmt.Errorf("losetup -d %s failed: %v", device, err)
	}
	return nil
}
removeLoopDevice removes specified loopback device
package volumepathhandler
import (
	"bufio"
	"context"
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	pmemlog "github.com/intel/pmem-csi/pkg/logger"
	"golang.org/x/sys/unix"
	"k8s.io/apimachinery/pkg/types"
)
func (v VolumePathHandler) AttachFileDevice(ctx context.Context, path string) (string, error) {
	return v.AttachFileDeviceWithOffset(ctx, path, 0)
}
func (v VolumePathHandler) AttachFileDeviceWithOffset(ctx context.Context, path string, offset int64) (string, error) {
	ctx, logger := pmemlog.WithName(ctx, "AttachFileDevice")
	blockDevicePath, err := v.GetLoopDevice(ctx, path)
	if err != nil && err.Error() != ErrDeviceNotFound {
		return "", fmt.Errorf("GetLoopDevice failed for path %s: %v", path, err)
	}
	if blockDevicePath == "" {
		logger.V(4).Info("Creating device", "path", path)
		blockDevicePath, err = makeLoopDevice(ctx, path, offset)
		if err != nil {
			return "", fmt.Errorf("makeLoopDevice failed for path %s: %v", path, err)
		}
	}
	return blockDevicePath, nil
}
func (v VolumePathHandler) DetachFileDevice(ctx context.Context, path string) error {
	ctx, logger := pmemlog.WithName(ctx, "DetachFileDevice")
	loopPath, err := v.GetLoopDevice(ctx, path)
	if err != nil {
		if err.Error() == ErrDeviceNotFound {
			logger.V(2).Info("couldn't find loopback device which takes file descriptor lock. Skip detaching device.", "path", path)
		} else {
			return fmt.Errorf("GetLoopDevice failed for path %s: %v", path, err)
		}
	} else {
		if len(loopPath) != 0 {
			err = removeLoopDevice(ctx, loopPath)
			if err != nil {
				return fmt.Errorf("removeLoopDevice failed for path %s: %v", path, err)
			}
		}
	}
	return nil
}
func (v VolumePathHandler) GetLoopDevice(ctx context.Context, path string) (string, error) {
	ctx, logger := pmemlog.WithName(ctx, "GetLoopDevice")
	_, err := os.Stat(path)
	if os.IsNotExist(err) {
		logger.V(2).Info("Loop device backing file not found, assuming loop device does not exist either", "path", path)
		return "", errors.New(ErrDeviceNotFound)
	}
	if err != nil {
		return "", fmt.Errorf("not attachable: %v", err)
	}
	args := []string{"-j", path}
	cmd := exec.Command(losetupPath, args...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		logger.V(2).Info("Failed device discover command", "path", path, "error", err, "stdout", string(out))
		return "", fmt.Errorf("losetup -j %s failed: %v", path, err)
	}
	logger.V(5).Info("losetup -j", "path", path, "stdout", string(out))
	return parseLosetupOutputForDevice(out, path)
}
func makeLoopDevice(ctx context.Context, path string, offset int64) (string, error) {
	ctx, logger := pmemlog.WithName(ctx, "makeLoopDevice")
	args := []string{"-f", "--show"}
	if offset != 0 {
		args = append(args, "-o", fmt.Sprintf("%d", offset))
	}
	args = append(args, path)
	cmd := exec.Command(losetupPath, args...)
	out, err := cmd.CombinedOutput()
	if err != nil {
		logger.V(2).Info("Failed device create command", "path", path, "error", err, "stdout", out)
		return "", fmt.Errorf("losetup -f --show %s failed: %v", path, err)
	}
	if len(out) == 0 {
		return "", errors.New(ErrDeviceNotFound)
	}
	return strings.TrimSpace(string(out)), nil
}