github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/pkg/specgen/volumes.go (about) 1 package specgen 2 3 import ( 4 "path/filepath" 5 "strings" 6 7 "github.com/containers/buildah/pkg/parse" 8 spec "github.com/opencontainers/runtime-spec/specs-go" 9 "github.com/pkg/errors" 10 "github.com/sirupsen/logrus" 11 ) 12 13 // NamedVolume holds information about a named volume that will be mounted into 14 // the container. 15 type NamedVolume struct { 16 // Name is the name of the named volume to be mounted. May be empty. 17 // If empty, a new named volume with a pseudorandomly generated name 18 // will be mounted at the given destination. 19 Name string 20 // Destination to mount the named volume within the container. Must be 21 // an absolute path. Path will be created if it does not exist. 22 Dest string 23 // Options are options that the named volume will be mounted with. 24 Options []string 25 } 26 27 // OverlayVolume holds information about a overlay volume that will be mounted into 28 // the container. 29 type OverlayVolume struct { 30 // Destination is the absolute path where the mount will be placed in the container. 31 Destination string `json:"destination"` 32 // Source specifies the source path of the mount. 33 Source string `json:"source,omitempty"` 34 } 35 36 // ImageVolume is a volume based on a container image. The container image is 37 // first mounted on the host and is then bind-mounted into the container. An 38 // ImageVolume is always mounted read only. 39 type ImageVolume struct { 40 // Source is the source of the image volume. The image can be referred 41 // to by name and by ID. 42 Source string 43 // Destination is the absolute path of the mount in the container. 44 Destination string 45 // ReadWrite sets the volume writable. 46 ReadWrite bool 47 } 48 49 // GenVolumeMounts parses user input into mounts, volumes and overlay volumes 50 func GenVolumeMounts(volumeFlag []string) (map[string]spec.Mount, map[string]*NamedVolume, map[string]*OverlayVolume, error) { 51 errDuplicateDest := errors.Errorf("duplicate mount destination") 52 53 mounts := make(map[string]spec.Mount) 54 volumes := make(map[string]*NamedVolume) 55 overlayVolumes := make(map[string]*OverlayVolume) 56 57 volumeFormatErr := errors.Errorf("incorrect volume format, should be [host-dir:]ctr-dir[:option]") 58 59 for _, vol := range volumeFlag { 60 var ( 61 options []string 62 src string 63 dest string 64 err error 65 ) 66 67 splitVol := strings.Split(vol, ":") 68 if len(splitVol) > 3 { 69 return nil, nil, nil, errors.Wrapf(volumeFormatErr, vol) 70 } 71 72 src = splitVol[0] 73 if len(splitVol) == 1 { 74 // This is an anonymous named volume. Only thing given 75 // is destination. 76 // Name/source will be blank, and populated by libpod. 77 src = "" 78 dest = splitVol[0] 79 } else if len(splitVol) > 1 { 80 dest = splitVol[1] 81 } 82 if len(splitVol) > 2 { 83 if options, err = parse.ValidateVolumeOpts(strings.Split(splitVol[2], ",")); err != nil { 84 return nil, nil, nil, err 85 } 86 } 87 88 // Do not check source dir for anonymous volumes 89 if len(splitVol) > 1 { 90 if len(src) == 0 { 91 return nil, nil, nil, errors.New("host directory cannot be empty") 92 } 93 } 94 if err := parse.ValidateVolumeCtrDir(dest); err != nil { 95 return nil, nil, nil, err 96 } 97 98 cleanDest := filepath.Clean(dest) 99 100 if strings.HasPrefix(src, "/") || strings.HasPrefix(src, ".") { 101 // This is not a named volume 102 overlayFlag := false 103 for _, o := range options { 104 if o == "O" { 105 overlayFlag = true 106 if len(options) > 1 { 107 return nil, nil, nil, errors.New("can't use 'O' with other options") 108 } 109 } 110 } 111 if overlayFlag { 112 // This is a overlay volume 113 newOverlayVol := new(OverlayVolume) 114 newOverlayVol.Destination = cleanDest 115 newOverlayVol.Source = src 116 if _, ok := overlayVolumes[newOverlayVol.Destination]; ok { 117 return nil, nil, nil, errors.Wrapf(errDuplicateDest, newOverlayVol.Destination) 118 } 119 overlayVolumes[newOverlayVol.Destination] = newOverlayVol 120 } else { 121 newMount := spec.Mount{ 122 Destination: cleanDest, 123 Type: "bind", 124 Source: src, 125 Options: options, 126 } 127 if _, ok := mounts[newMount.Destination]; ok { 128 return nil, nil, nil, errors.Wrapf(errDuplicateDest, newMount.Destination) 129 } 130 mounts[newMount.Destination] = newMount 131 } 132 } else { 133 // This is a named volume 134 newNamedVol := new(NamedVolume) 135 newNamedVol.Name = src 136 newNamedVol.Dest = cleanDest 137 newNamedVol.Options = options 138 139 if _, ok := volumes[newNamedVol.Dest]; ok { 140 return nil, nil, nil, errors.Wrapf(errDuplicateDest, newNamedVol.Dest) 141 } 142 volumes[newNamedVol.Dest] = newNamedVol 143 } 144 145 logrus.Debugf("User mount %s:%s options %v", src, dest, options) 146 } 147 148 return mounts, volumes, overlayVolumes, nil 149 }