github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/mount/loop/losetup_linux.go (about)

     1  // Copyright 2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package loop
     6  
     7  import (
     8  	"fmt"
     9  	"os"
    10  
    11  	"golang.org/x/sys/unix"
    12  )
    13  
    14  // FindDevice finds an unused loop device and returns its /dev/loopN path.
    15  func FindDevice() (string, error) {
    16  	cfd, err := os.OpenFile("/dev/loop-control", os.O_RDWR, 0o644)
    17  	if err != nil {
    18  		return "", err
    19  	}
    20  	defer cfd.Close()
    21  
    22  	number, err := GetFree(int(cfd.Fd()))
    23  	if err != nil {
    24  		return "", err
    25  	}
    26  	return fmt.Sprintf("/dev/loop%d", number), nil
    27  }
    28  
    29  // ClearFD clears the loop device associated with file descriptor fd.
    30  func ClearFD(fd int) error {
    31  	return unix.IoctlSetInt(fd, unix.LOOP_CLR_FD, 0)
    32  }
    33  
    34  // GetFree finds a free loop device /dev/loopN.
    35  //
    36  // fd must be a loop control device.
    37  //
    38  // It returns the number of the free loop device /dev/loopN.
    39  // The _LOOP_CTL_GET_FREE does not follow the rules. Values
    40  // of 0 or greater are the number of the device; less than
    41  // zero is an error.
    42  // So you can not use unix.IoctlGetInt as it assumes the return
    43  // value is stored in a pointer in the normal style. Yuck.
    44  func GetFree(fd int) (int, error) {
    45  	return unix.IoctlRetInt(fd, unix.LOOP_CTL_GET_FREE)
    46  }
    47  
    48  // SetFD associates a loop device lfd with a regular file ffd.
    49  func SetFD(lfd, ffd int) error {
    50  	return unix.IoctlSetInt(lfd, unix.LOOP_SET_FD, ffd)
    51  }
    52  
    53  // SetFile associates loop device "devicename" with regular file "filename"
    54  func SetFile(devicename, filename string) error {
    55  	mode := os.O_RDWR
    56  	file, err := os.OpenFile(filename, mode, 0o644)
    57  	if err != nil {
    58  		mode = os.O_RDONLY
    59  		file, err = os.OpenFile(filename, mode, 0o644)
    60  		if err != nil {
    61  			return err
    62  		}
    63  	}
    64  	defer file.Close()
    65  
    66  	device, err := os.OpenFile(devicename, mode, 0o644)
    67  	if err != nil {
    68  		return err
    69  	}
    70  	defer device.Close()
    71  
    72  	return SetFD(int(device.Fd()), int(file.Fd()))
    73  }
    74  
    75  // ClearFile clears the fd association of the loop device "devicename".
    76  func ClearFile(devicename string) error {
    77  	device, err := os.Open(devicename)
    78  	if err != nil {
    79  		return err
    80  	}
    81  	defer device.Close()
    82  
    83  	return ClearFD(int(device.Fd()))
    84  }