github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/libpod/volume_internal_linux.go (about)

     1  // +build linux
     2  
     3  package libpod
     4  
     5  import (
     6  	"os/exec"
     7  	"strings"
     8  
     9  	"github.com/containers/libpod/libpod/define"
    10  	"github.com/containers/libpod/pkg/rootless"
    11  	"github.com/pkg/errors"
    12  	"github.com/sirupsen/logrus"
    13  	"golang.org/x/sys/unix"
    14  )
    15  
    16  // mount mounts the volume if necessary.
    17  // A mount is necessary if a volume has any options set.
    18  // If a mount is necessary, v.state.MountCount will be incremented.
    19  // If it was 0 when the increment occurred, the volume will be mounted on the
    20  // host. Otherwise, we assume it is already mounted.
    21  // Must be done while the volume is locked.
    22  // Is a no-op on volumes that do not require a mount (as defined by
    23  // volumeNeedsMount())
    24  func (v *Volume) mount() error {
    25  	if !v.needsMount() {
    26  		return nil
    27  	}
    28  
    29  	// We cannot mount volumes as rootless.
    30  	if rootless.IsRootless() {
    31  		return errors.Wrapf(define.ErrRootless, "cannot mount volumes without root privileges")
    32  	}
    33  
    34  	// Update the volume from the DB to get an accurate mount counter.
    35  	if err := v.update(); err != nil {
    36  		return err
    37  	}
    38  
    39  	// If the count is non-zero, the volume is already mounted.
    40  	// Nothing to do.
    41  	if v.state.MountCount > 0 {
    42  		v.state.MountCount += 1
    43  		logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount)
    44  		return v.save()
    45  	}
    46  
    47  	volDevice := v.config.Options["device"]
    48  	volType := v.config.Options["type"]
    49  	volOptions := v.config.Options["o"]
    50  
    51  	// Some filesystems (tmpfs) don't have a device, but we still need to
    52  	// give the kernel something.
    53  	if volDevice == "" && volType != "" {
    54  		volDevice = volType
    55  	}
    56  
    57  	// We need to use the actual mount command.
    58  	// Convincing unix.Mount to use the same semantics as the mount command
    59  	// itself seems prohibitively difficult.
    60  	// TODO: might want to cache this path in the runtime?
    61  	mountPath, err := exec.LookPath("mount")
    62  	if err != nil {
    63  		return errors.Wrapf(err, "error locating 'mount' binary")
    64  	}
    65  	mountArgs := []string{}
    66  	if volOptions != "" {
    67  		mountArgs = append(mountArgs, "-o", volOptions)
    68  	}
    69  	if volType != "" {
    70  		mountArgs = append(mountArgs, "-t", volType)
    71  	}
    72  	mountArgs = append(mountArgs, volDevice, v.config.MountPoint)
    73  	mountCmd := exec.Command(mountPath, mountArgs...)
    74  
    75  	logrus.Debugf("Running mount command: %s %s", mountPath, strings.Join(mountArgs, " "))
    76  	if output, err := mountCmd.CombinedOutput(); err != nil {
    77  		logrus.Debugf("Mount failed with %v", err)
    78  		return errors.Wrapf(errors.Errorf(string(output)), "error mounting volume %s", v.Name())
    79  	}
    80  
    81  	logrus.Debugf("Mounted volume %s", v.Name())
    82  
    83  	// Increment the mount counter
    84  	v.state.MountCount += 1
    85  	logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount)
    86  	return v.save()
    87  }
    88  
    89  // unmount unmounts the volume if necessary.
    90  // Unmounting a volume that is not mounted is a no-op.
    91  // Unmounting a volume that does not require a mount is a no-op.
    92  // The volume must be locked for this to occur.
    93  // The mount counter will be decremented if non-zero. If the counter reaches 0,
    94  // the volume will really be unmounted, as no further containers are using the
    95  // volume.
    96  // If force is set, the volume will be unmounted regardless of mount counter.
    97  func (v *Volume) unmount(force bool) error {
    98  	if !v.needsMount() {
    99  		return nil
   100  	}
   101  
   102  	// Update the volume from the DB to get an accurate mount counter.
   103  	if err := v.update(); err != nil {
   104  		return err
   105  	}
   106  
   107  	if v.state.MountCount == 0 {
   108  		logrus.Debugf("Volume %s already unmounted", v.Name())
   109  		return nil
   110  	}
   111  
   112  	// We cannot unmount volumes as rootless.
   113  	if rootless.IsRootless() {
   114  		// If force is set, just clear the counter and bail without
   115  		// error, so we can remove volumes from the state if they are in
   116  		// an awkward configuration.
   117  		if force {
   118  			logrus.Errorf("Volume %s is mounted despite being rootless - state is not sane", v.Name())
   119  			v.state.MountCount = 0
   120  			return v.save()
   121  		}
   122  
   123  		return errors.Wrapf(define.ErrRootless, "cannot mount or unmount volumes without root privileges")
   124  	}
   125  
   126  	if !force {
   127  		v.state.MountCount -= 1
   128  	} else {
   129  		v.state.MountCount = 0
   130  	}
   131  
   132  	logrus.Debugf("Volume %s mount count now at %d", v.Name(), v.state.MountCount)
   133  
   134  	if v.state.MountCount == 0 {
   135  		// Unmount the volume
   136  		if err := unix.Unmount(v.config.MountPoint, unix.MNT_DETACH); err != nil {
   137  			if err == unix.EINVAL {
   138  				// Ignore EINVAL - the mount no longer exists.
   139  				return nil
   140  			}
   141  			return errors.Wrapf(err, "error unmounting volume %s", v.Name())
   142  		}
   143  		logrus.Debugf("Unmounted volume %s", v.Name())
   144  	}
   145  
   146  	return v.save()
   147  }