github.com/hpcng/singularity@v3.1.1+incompatible/pkg/util/loop/loop_linux.go (about) 1 // Copyright (c) 2018-2019, Sylabs Inc. All rights reserved. 2 // This software is licensed under a 3-clause BSD license. Please consult the 3 // LICENSE.md file distributed with the sources of this project regarding your 4 // rights to use or distribute this software. 5 6 package loop 7 8 import ( 9 "fmt" 10 "os" 11 "syscall" 12 "unsafe" 13 14 "github.com/sylabs/singularity/pkg/util/fs/lock" 15 ) 16 17 // AttachFromFile finds a free loop device, opens it, and stores file descriptor 18 // provided by image file pointer 19 func (loop *Device) AttachFromFile(image *os.File, mode int, number *int) error { 20 var path string 21 var loopFd int 22 23 if image == nil { 24 return fmt.Errorf("empty file pointer") 25 } 26 27 fi, err := image.Stat() 28 if err != nil { 29 return err 30 } 31 st := fi.Sys().(*syscall.Stat_t) 32 imageIno := st.Ino 33 imageDev := st.Dev 34 35 fd, err := lock.Exclusive("/dev") 36 if err != nil { 37 return err 38 } 39 defer lock.Release(fd) 40 41 freeDevice := -1 42 43 for device := 0; device <= loop.MaxLoopDevices; device++ { 44 *number = device 45 46 if device == loop.MaxLoopDevices { 47 if loop.Shared { 48 loop.Shared = false 49 if freeDevice != -1 { 50 device = freeDevice 51 continue 52 } 53 } 54 return fmt.Errorf("no loop devices available") 55 } 56 57 path = fmt.Sprintf("/dev/loop%d", device) 58 if fi, err := os.Stat(path); err != nil { 59 dev := int((7 << 8) | (device & 0xff) | ((device & 0xfff00) << 12)) 60 esys := syscall.Mknod(path, syscall.S_IFBLK|0660, dev) 61 if errno, ok := esys.(syscall.Errno); ok { 62 if errno != syscall.EEXIST { 63 return esys 64 } 65 } 66 } else if fi.Mode()&os.ModeDevice == 0 { 67 return fmt.Errorf("%s is not a block device", path) 68 } 69 70 if loopFd, err = syscall.Open(path, mode, 0600); err != nil { 71 continue 72 } 73 if loop.Shared { 74 status, err := GetStatusFromFd(uintptr(loopFd)) 75 syscall.Close(loopFd) 76 if err != nil { 77 return err 78 } 79 // there is no associated image with loop device, save indice so second loop 80 // iteration will start from this device 81 if status.Inode == 0 && freeDevice == -1 { 82 freeDevice = device 83 continue 84 } 85 if status.Inode == imageIno && status.Device == imageDev && 86 status.Flags&FlagsReadOnly == loop.Info.Flags&FlagsReadOnly && 87 status.Offset == loop.Info.Offset && status.SizeLimit == loop.Info.SizeLimit { 88 return nil 89 } 90 } else { 91 _, _, esys := syscall.Syscall(syscall.SYS_IOCTL, uintptr(loopFd), CmdSetFd, image.Fd()) 92 if esys != 0 { 93 syscall.Close(loopFd) 94 continue 95 } 96 break 97 } 98 } 99 100 if _, _, err := syscall.Syscall(syscall.SYS_FCNTL, uintptr(loopFd), syscall.F_SETFD, syscall.FD_CLOEXEC); err != 0 { 101 return fmt.Errorf("failed to set close-on-exec on loop device %s: %s", path, err.Error()) 102 } 103 104 if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, uintptr(loopFd), CmdSetStatus64, uintptr(unsafe.Pointer(loop.Info))); err != 0 { 105 return fmt.Errorf("Failed to set loop flags on loop device: %s", syscall.Errno(err)) 106 } 107 108 return nil 109 } 110 111 // AttachFromPath finds a free loop device, opens it, and stores file descriptor 112 // of opened image path 113 func (loop *Device) AttachFromPath(image string, mode int, number *int) error { 114 file, err := os.OpenFile(image, mode, 0600) 115 if err != nil { 116 return err 117 } 118 return loop.AttachFromFile(file, mode, number) 119 } 120 121 // GetStatusFromFd gets info status about an opened loop device 122 func GetStatusFromFd(fd uintptr) (*Info64, error) { 123 info := &Info64{} 124 _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, CmdGetStatus64, uintptr(unsafe.Pointer(info))) 125 if err != syscall.ENXIO && err != 0 { 126 return nil, fmt.Errorf("Failed to get loop flags for loop device: %s", err.Error()) 127 } 128 return info, nil 129 } 130 131 // GetStatusFromPath gets info status about a loop device from path 132 func GetStatusFromPath(path string) (*Info64, error) { 133 loop, err := os.Open(path) 134 if err != nil { 135 return nil, fmt.Errorf("failed to open loop device %s: %s", path, err) 136 } 137 return GetStatusFromFd(loop.Fd()) 138 }