1
//go:build !remote && (linux || freebsd)
11
"github.com/containers/podman/v5/libpod/define"
12
pluginapi "github.com/docker/go-plugins-helpers/volume"
13
"github.com/sirupsen/logrus"
14
"golang.org/x/sys/unix"
17
// This is a pseudo-container ID to use when requesting a mount or unmount from
19
// This is the shas256 of the string "placeholder\n".
20
const pseudoCtrID = "2f73349cfc4630255319c6c8dfc1b46a8996ace9d14d8e07563b165915918ec2"
22
// mount mounts the volume if necessary.
23
// A mount is necessary if a volume has any options set.
24
// If a mount is necessary, v.state.MountCount will be incremented.
25
// If it was 0 when the increment occurred, the volume will be mounted on the
26
// host. Otherwise, we assume it is already mounted.
27
// Must be done while the volume is locked.
28
// Is a no-op on volumes that do not require a mount (as defined by
29
// volumeNeedsMount()).
30
func (v *Volume) mount() error {
35
// Update the volume from the DB to get an accurate mount counter.
36
if err := v.update(); err != nil {
40
// If the count is non-zero, the volume is already mounted.
42
if v.state.MountCount > 0 {
44
logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount)
48
// Volume plugins implement their own mount counter, based on the ID of
49
// the mounting container. But we already have one, and honestly I trust
50
// ours more. So hardcode container ID to something reasonable, and use
51
// the same one for everything.
52
if v.UsesVolumeDriver() {
54
return fmt.Errorf("volume plugin %s (needed by volume %s) missing: %w", v.Driver(), v.Name(), define.ErrMissingPlugin)
57
req := new(pluginapi.MountRequest)
60
mountPoint, err := v.plugin.MountVolume(req)
66
v.state.MountPoint = mountPoint
68
} else if v.config.Driver == define.VolumeDriverImage {
69
mountPoint, err := v.runtime.storageService.MountContainerImage(v.config.StorageID)
71
return fmt.Errorf("mounting volume %s image failed: %w", v.Name(), err)
75
v.state.MountPoint = mountPoint
79
volDevice := v.config.Options["device"]
80
volType := v.config.Options["type"]
81
volOptions := v.config.Options["o"]
83
// Some filesystems (tmpfs) don't have a device, but we still need to
84
// give the kernel something.
85
if volDevice == "" && volType != "" {
89
// We need to use the actual mount command.
90
// Convincing unix.Mount to use the same semantics as the mount command
91
// itself seems prohibitively difficult.
92
// TODO: might want to cache this path in the runtime?
93
mountPath, err := exec.LookPath("mount")
95
return fmt.Errorf("locating 'mount' binary: %w", err)
97
mountArgs := []string{}
99
mountArgs = append(mountArgs, "-o", volOptions)
103
case define.TypeBind:
104
mountArgs = append(mountArgs, "-o", volType)
106
mountArgs = append(mountArgs, "-t", volType)
109
mountArgs = append(mountArgs, volDevice, v.config.MountPoint)
110
mountCmd := exec.Command(mountPath, mountArgs...)
112
logrus.Debugf("Running mount command: %s %s", mountPath, strings.Join(mountArgs, " "))
113
if output, err := mountCmd.CombinedOutput(); err != nil {
114
logrus.Debugf("Mount %v failed with %v", mountCmd, err)
115
return errors.New(string(output))
118
logrus.Debugf("Mounted volume %s", v.Name())
120
// Increment the mount counter
122
logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount)
126
// unmount unmounts the volume if necessary.
127
// Unmounting a volume that is not mounted is a no-op.
128
// Unmounting a volume that does not require a mount is a no-op.
129
// The volume must be locked for this to occur.
130
// The mount counter will be decremented if non-zero. If the counter reaches 0,
131
// the volume will really be unmounted, as no further containers are using the
133
// If force is set, the volume will be unmounted regardless of mount counter.
134
func (v *Volume) unmount(force bool) error {
139
// Update the volume from the DB to get an accurate mount counter.
140
if err := v.update(); err != nil {
144
if v.state.MountCount == 0 {
145
logrus.Debugf("Volume %s already unmounted", v.Name())
152
v.state.MountCount = 0
155
logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount)
157
if v.state.MountCount == 0 {
158
if v.UsesVolumeDriver() {
160
return fmt.Errorf("volume plugin %s (needed by volume %s) missing: %w", v.Driver(), v.Name(), define.ErrMissingPlugin)
163
req := new(pluginapi.UnmountRequest)
166
if err := v.plugin.UnmountVolume(req); err != nil {
170
v.state.MountPoint = ""
172
} else if v.config.Driver == define.VolumeDriverImage {
173
if _, err := v.runtime.storageService.UnmountContainerImage(v.config.StorageID, force); err != nil {
174
return fmt.Errorf("unmounting volume %s image: %w", v.Name(), err)
177
v.state.MountPoint = ""
181
// Unmount the volume
182
if err := detachUnmount(v.config.MountPoint); err != nil {
183
if err == unix.EINVAL {
184
// Ignore EINVAL - the mount no longer exists.
187
return fmt.Errorf("unmounting volume %s: %w", v.Name(), err)
189
logrus.Debugf("Unmounted volume %s", v.Name())