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 }