github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/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  const (
    15  	// Loop ioctl commands --- we will commandeer 0x4C ('L')
    16  	_LOOP_SET_CAPACITY = 0x4C07
    17  	_LOOP_CHANGE_FD    = 0x4C06
    18  	_LOOP_GET_STATUS64 = 0x4C05
    19  	_LOOP_SET_STATUS64 = 0x4C04
    20  	_LOOP_GET_STATUS   = 0x4C03
    21  	_LOOP_SET_STATUS   = 0x4C02
    22  	_LOOP_CLR_FD       = 0x4C01
    23  	_LOOP_SET_FD       = 0x4C00
    24  	_LO_NAME_SIZE      = 64
    25  	_LO_KEY_SIZE       = 32
    26  
    27  	// /dev/loop-control interface
    28  	_LOOP_CTL_ADD      = 0x4C80
    29  	_LOOP_CTL_REMOVE   = 0x4C81
    30  	_LOOP_CTL_GET_FREE = 0x4C82
    31  )
    32  
    33  // FindDevice finds an unused loop device and returns its /dev/loopN path.
    34  func FindDevice() (string, error) {
    35  	cfd, err := os.OpenFile("/dev/loop-control", os.O_RDWR, 0644)
    36  	if err != nil {
    37  		return "", err
    38  	}
    39  	defer cfd.Close()
    40  
    41  	number, err := GetFree(int(cfd.Fd()))
    42  	if err != nil {
    43  		return "", err
    44  	}
    45  	return fmt.Sprintf("/dev/loop%d", number), nil
    46  }
    47  
    48  // ClearFD clears the loop device associated with file descriptor fd.
    49  func ClearFD(fd int) error {
    50  	return unix.IoctlSetInt(fd, _LOOP_CLR_FD, 0)
    51  }
    52  
    53  // GetFree finds a free loop device /dev/loopN.
    54  //
    55  // fd must be a loop control device.
    56  //
    57  // It returns the number of the free loop device /dev/loopN.
    58  // The _LOOP_CTL_GET_FREE does not follow the rules. Values
    59  // of 0 or greater are the number of the device; less than
    60  // zero is an error.
    61  // So you can not use unix.IoctlGetInt as it assumes the return
    62  // value is stored in a pointer in the normal style. Yuck.
    63  func GetFree(fd int) (int, error) {
    64  	r1, _, err := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), _LOOP_CTL_GET_FREE, 0)
    65  	if err == 0 {
    66  		return int(r1), nil
    67  	}
    68  	return 0, err
    69  }
    70  
    71  // SetFD associates a loop device lfd with a regular file ffd.
    72  func SetFD(lfd, ffd int) error {
    73  	return unix.IoctlSetInt(lfd, _LOOP_SET_FD, ffd)
    74  }
    75  
    76  // SetFile associates loop device "devicename" with regular file "filename"
    77  func SetFile(devicename, filename string) error {
    78  	mode := os.O_RDWR
    79  	file, err := os.OpenFile(filename, mode, 0644)
    80  	if err != nil {
    81  		mode = os.O_RDONLY
    82  		file, err = os.OpenFile(filename, mode, 0644)
    83  		if err != nil {
    84  			return err
    85  		}
    86  	}
    87  	defer file.Close()
    88  
    89  	device, err := os.OpenFile(devicename, mode, 0644)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	defer device.Close()
    94  
    95  	return SetFD(int(device.Fd()), int(file.Fd()))
    96  }
    97  
    98  // ClearFile clears the fd association of the loop device "devicename".
    99  func ClearFile(devicename string) error {
   100  	device, err := os.Open(devicename)
   101  	if err != nil {
   102  		return err
   103  	}
   104  	defer device.Close()
   105  
   106  	return ClearFD(int(device.Fd()))
   107  }