1// +build linux,cgo
2
3package loopback // import "github.com/docker/docker/pkg/loopback"
4
5import (
6	"fmt"
7	"os"
8
9	"github.com/sirupsen/logrus"
10	"golang.org/x/sys/unix"
11)
12
13func getLoopbackBackingFile(file *os.File) (uint64, uint64, error) {
14	loopInfo, err := ioctlLoopGetStatus64(file.Fd())
15	if err != nil {
16		logrus.Errorf("Error get loopback backing file: %s", err)
17		return 0, 0, ErrGetLoopbackBackingFile
18	}
19	return loopInfo.Device, loopInfo.Inode, nil
20}
21
22// SetCapacity reloads the size for the loopback device.
23func SetCapacity(file *os.File) error {
24	if err := ioctlLoopSetCapacity(file.Fd(), 0); err != nil {
25		logrus.Errorf("Error loopbackSetCapacity: %s", err)
26		return ErrSetCapacity
27	}
28	return nil
29}
30
31// FindLoopDeviceFor returns a loopback device file for the specified file which
32// is backing file of a loop back device.
33func FindLoopDeviceFor(file *os.File) *os.File {
34	var stat unix.Stat_t
35	err := unix.Stat(file.Name(), &stat)
36	if err != nil {
37		return nil
38	}
39	targetInode := stat.Ino
40	// the type is 32bit on mips
41	targetDevice := uint64(stat.Dev) // nolint: unconvert
42
43	for i := 0; true; i++ {
44		path := fmt.Sprintf("/dev/loop%d", i)
45
46		file, err := os.OpenFile(path, os.O_RDWR, 0)
47		if err != nil {
48			if os.IsNotExist(err) {
49				return nil
50			}
51
52			// Ignore all errors until the first not-exist
53			// we want to continue looking for the file
54			continue
55		}
56
57		dev, inode, err := getLoopbackBackingFile(file)
58		if err == nil && dev == targetDevice && inode == targetInode {
59			return file
60		}
61		file.Close()
62	}
63
64	return nil
65}
66