aboutsummaryrefslogtreecommitdiff
path: root/libpod/volume_internal_common.go
diff options
context:
space:
mode:
Diffstat (limited to 'libpod/volume_internal_common.go')
-rw-r--r--libpod/volume_internal_common.go194
1 files changed, 194 insertions, 0 deletions
diff --git a/libpod/volume_internal_common.go b/libpod/volume_internal_common.go
new file mode 100644
index 000000000..440bceec3
--- /dev/null
+++ b/libpod/volume_internal_common.go
@@ -0,0 +1,194 @@
+//go:build linux
+// +build linux
+
+package libpod
+
+import (
+ "errors"
+ "fmt"
+ "os/exec"
+ "strings"
+
+ "github.com/containers/podman/v4/libpod/define"
+ pluginapi "github.com/docker/go-plugins-helpers/volume"
+ "github.com/sirupsen/logrus"
+ "golang.org/x/sys/unix"
+)
+
+// This is a pseudo-container ID to use when requesting a mount or unmount from
+// the volume plugins.
+// This is the shas256 of the string "placeholder\n".
+const pseudoCtrID = "2f73349cfc4630255319c6c8dfc1b46a8996ace9d14d8e07563b165915918ec2"
+
+// mount mounts the volume if necessary.
+// A mount is necessary if a volume has any options set.
+// If a mount is necessary, v.state.MountCount will be incremented.
+// If it was 0 when the increment occurred, the volume will be mounted on the
+// host. Otherwise, we assume it is already mounted.
+// Must be done while the volume is locked.
+// Is a no-op on volumes that do not require a mount (as defined by
+// volumeNeedsMount()).
+func (v *Volume) mount() error {
+ if !v.needsMount() {
+ return nil
+ }
+
+ // Update the volume from the DB to get an accurate mount counter.
+ if err := v.update(); err != nil {
+ return err
+ }
+
+ // If the count is non-zero, the volume is already mounted.
+ // Nothing to do.
+ if v.state.MountCount > 0 {
+ v.state.MountCount++
+ logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount)
+ return v.save()
+ }
+
+ // Volume plugins implement their own mount counter, based on the ID of
+ // the mounting container. But we already have one, and honestly I trust
+ // ours more. So hardcode container ID to something reasonable, and use
+ // the same one for everything.
+ if v.UsesVolumeDriver() {
+ if v.plugin == nil {
+ return fmt.Errorf("volume plugin %s (needed by volume %s) missing: %w", v.Driver(), v.Name(), define.ErrMissingPlugin)
+ }
+
+ req := new(pluginapi.MountRequest)
+ req.Name = v.Name()
+ req.ID = pseudoCtrID
+ mountPoint, err := v.plugin.MountVolume(req)
+ if err != nil {
+ return err
+ }
+
+ v.state.MountCount++
+ v.state.MountPoint = mountPoint
+ return v.save()
+ } else if v.config.Driver == define.VolumeDriverImage {
+ mountPoint, err := v.runtime.storageService.MountContainerImage(v.config.StorageID)
+ if err != nil {
+ return fmt.Errorf("mounting volume %s image failed: %w", v.Name(), err)
+ }
+
+ v.state.MountCount++
+ v.state.MountPoint = mountPoint
+ return v.save()
+ }
+
+ volDevice := v.config.Options["device"]
+ volType := v.config.Options["type"]
+ volOptions := v.config.Options["o"]
+
+ // Some filesystems (tmpfs) don't have a device, but we still need to
+ // give the kernel something.
+ if volDevice == "" && volType != "" {
+ volDevice = volType
+ }
+
+ // We need to use the actual mount command.
+ // Convincing unix.Mount to use the same semantics as the mount command
+ // itself seems prohibitively difficult.
+ // TODO: might want to cache this path in the runtime?
+ mountPath, err := exec.LookPath("mount")
+ if err != nil {
+ return fmt.Errorf("locating 'mount' binary: %w", err)
+ }
+ mountArgs := []string{}
+ if volOptions != "" {
+ mountArgs = append(mountArgs, "-o", volOptions)
+ }
+ switch volType {
+ case "":
+ case "bind":
+ mountArgs = append(mountArgs, "-o", volType)
+ default:
+ mountArgs = append(mountArgs, "-t", volType)
+ }
+
+ mountArgs = append(mountArgs, volDevice, v.config.MountPoint)
+ mountCmd := exec.Command(mountPath, mountArgs...)
+
+ logrus.Debugf("Running mount command: %s %s", mountPath, strings.Join(mountArgs, " "))
+ if output, err := mountCmd.CombinedOutput(); err != nil {
+ logrus.Debugf("Mount %v failed with %v", mountCmd, err)
+ return errors.New(string(output))
+ }
+
+ logrus.Debugf("Mounted volume %s", v.Name())
+
+ // Increment the mount counter
+ v.state.MountCount++
+ logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount)
+ return v.save()
+}
+
+// unmount unmounts the volume if necessary.
+// Unmounting a volume that is not mounted is a no-op.
+// Unmounting a volume that does not require a mount is a no-op.
+// The volume must be locked for this to occur.
+// The mount counter will be decremented if non-zero. If the counter reaches 0,
+// the volume will really be unmounted, as no further containers are using the
+// volume.
+// If force is set, the volume will be unmounted regardless of mount counter.
+func (v *Volume) unmount(force bool) error {
+ if !v.needsMount() {
+ return nil
+ }
+
+ // Update the volume from the DB to get an accurate mount counter.
+ if err := v.update(); err != nil {
+ return err
+ }
+
+ if v.state.MountCount == 0 {
+ logrus.Debugf("Volume %s already unmounted", v.Name())
+ return nil
+ }
+
+ if !force {
+ v.state.MountCount--
+ } else {
+ v.state.MountCount = 0
+ }
+
+ logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount)
+
+ if v.state.MountCount == 0 {
+ if v.UsesVolumeDriver() {
+ if v.plugin == nil {
+ return fmt.Errorf("volume plugin %s (needed by volume %s) missing: %w", v.Driver(), v.Name(), define.ErrMissingPlugin)
+ }
+
+ req := new(pluginapi.UnmountRequest)
+ req.Name = v.Name()
+ req.ID = pseudoCtrID
+ if err := v.plugin.UnmountVolume(req); err != nil {
+ return err
+ }
+
+ v.state.MountPoint = ""
+ return v.save()
+ } else if v.config.Driver == define.VolumeDriverImage {
+ if _, err := v.runtime.storageService.UnmountContainerImage(v.config.StorageID, force); err != nil {
+ return fmt.Errorf("unmounting volume %s image: %w", v.Name(), err)
+ }
+
+ v.state.MountPoint = ""
+ return v.save()
+ }
+
+ // Unmount the volume
+ if err := unix.Unmount(v.config.MountPoint, unix.MNT_DETACH); err != nil {
+ if err == unix.EINVAL {
+ // Ignore EINVAL - the mount no longer exists.
+ return nil
+ }
+ return fmt.Errorf("unmounting volume %s: %w", v.Name(), err)
+ }
+ logrus.Debugf("Unmounted volume %s", v.Name())
+ }
+
+ return v.save()
+}