github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/specgen/generate/storage.go (about) 1 package generate 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path" 8 "path/filepath" 9 "strings" 10 11 "github.com/containers/common/libimage" 12 "github.com/containers/common/pkg/config" 13 "github.com/containers/common/pkg/parse" 14 "github.com/hanks177/podman/v4/libpod" 15 "github.com/hanks177/podman/v4/libpod/define" 16 "github.com/hanks177/podman/v4/pkg/specgen" 17 "github.com/hanks177/podman/v4/pkg/util" 18 spec "github.com/opencontainers/runtime-spec/specs-go" 19 "github.com/pkg/errors" 20 "github.com/sirupsen/logrus" 21 ) 22 23 var errDuplicateDest = errors.Errorf("duplicate mount destination") 24 25 // Produce final mounts and named volumes for a container 26 func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runtime, rtc *config.Config, img *libimage.Image) ([]spec.Mount, []*specgen.NamedVolume, []*specgen.OverlayVolume, error) { 27 // Get image volumes 28 baseMounts, baseVolumes, err := getImageVolumes(ctx, img, s) 29 if err != nil { 30 return nil, nil, nil, err 31 } 32 33 // Get volumes-from mounts 34 volFromMounts, volFromVolumes, err := getVolumesFrom(s.VolumesFrom, rt) 35 if err != nil { 36 return nil, nil, nil, err 37 } 38 39 // Supersede from --volumes-from. 40 for dest, mount := range volFromMounts { 41 baseMounts[dest] = mount 42 } 43 for dest, volume := range volFromVolumes { 44 baseVolumes[dest] = volume 45 } 46 47 // Need to make map forms of specgen mounts/volumes. 48 unifiedMounts := map[string]spec.Mount{} 49 unifiedVolumes := map[string]*specgen.NamedVolume{} 50 unifiedOverlays := map[string]*specgen.OverlayVolume{} 51 52 // Need to make map forms of specgen mounts/volumes. 53 commonMounts, commonVolumes, commonOverlayVolumes, err := specgen.GenVolumeMounts(rtc.Volumes()) 54 if err != nil { 55 return nil, nil, nil, err 56 } 57 58 for _, m := range s.Mounts { 59 // Ensure that mount dest is clean, so that it can be 60 // compared against named volumes and avoid duplicate mounts. 61 if err = parse.ValidateVolumeCtrDir(m.Destination); err != nil { 62 return nil, nil, nil, err 63 } 64 cleanDestination := filepath.Clean(m.Destination) 65 if _, ok := unifiedMounts[cleanDestination]; ok { 66 return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", cleanDestination) 67 } 68 unifiedMounts[cleanDestination] = m 69 } 70 71 for _, m := range commonMounts { 72 if err = parse.ValidateVolumeCtrDir(m.Destination); err != nil { 73 return nil, nil, nil, err 74 } 75 cleanDestination := filepath.Clean(m.Destination) 76 if _, ok := unifiedMounts[cleanDestination]; !ok { 77 unifiedMounts[cleanDestination] = m 78 } 79 } 80 81 for _, v := range s.Volumes { 82 if err = parse.ValidateVolumeCtrDir(v.Dest); err != nil { 83 return nil, nil, nil, err 84 } 85 cleanDestination := filepath.Clean(v.Dest) 86 if _, ok := unifiedVolumes[cleanDestination]; ok { 87 return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination) 88 } 89 unifiedVolumes[cleanDestination] = v 90 } 91 92 for _, v := range commonVolumes { 93 if err = parse.ValidateVolumeCtrDir(v.Dest); err != nil { 94 return nil, nil, nil, err 95 } 96 cleanDestination := filepath.Clean(v.Dest) 97 if _, ok := unifiedVolumes[cleanDestination]; !ok { 98 unifiedVolumes[cleanDestination] = v 99 } 100 } 101 102 for _, v := range s.OverlayVolumes { 103 if err = parse.ValidateVolumeCtrDir(v.Destination); err != nil { 104 return nil, nil, nil, err 105 } 106 cleanDestination := filepath.Clean(v.Destination) 107 if _, ok := unifiedOverlays[cleanDestination]; ok { 108 return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified volumes - multiple volumes at %q", cleanDestination) 109 } 110 unifiedOverlays[cleanDestination] = v 111 } 112 113 for _, v := range commonOverlayVolumes { 114 if err = parse.ValidateVolumeCtrDir(v.Destination); err != nil { 115 return nil, nil, nil, err 116 } 117 cleanDestination := filepath.Clean(v.Destination) 118 if _, ok := unifiedOverlays[cleanDestination]; !ok { 119 unifiedOverlays[cleanDestination] = v 120 } 121 } 122 123 // If requested, add container init binary 124 if s.Init { 125 initPath := s.InitPath 126 if initPath == "" && rtc != nil { 127 initPath = rtc.Engine.InitPath 128 } 129 initMount, err := addContainerInitBinary(s, initPath) 130 if err != nil { 131 return nil, nil, nil, err 132 } 133 if _, ok := unifiedMounts[initMount.Destination]; ok { 134 return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict with mount added by --init to %q", initMount.Destination) 135 } 136 unifiedMounts[initMount.Destination] = initMount 137 } 138 139 // Before superseding, we need to find volume mounts which conflict with 140 // named volumes, and vice versa. 141 // We'll delete the conflicts here as we supersede. 142 for dest := range unifiedMounts { 143 delete(baseVolumes, dest) 144 } 145 for dest := range unifiedVolumes { 146 delete(baseMounts, dest) 147 } 148 149 // Supersede volumes-from/image volumes with unified volumes from above. 150 // This is an unconditional replacement. 151 for dest, mount := range unifiedMounts { 152 baseMounts[dest] = mount 153 } 154 for dest, volume := range unifiedVolumes { 155 baseVolumes[dest] = volume 156 } 157 158 // TODO: Investigate moving readonlyTmpfs into here. Would be more 159 // correct. 160 161 // Check for conflicts between named volumes and mounts 162 for dest := range baseMounts { 163 if _, ok := baseVolumes[dest]; ok { 164 return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) 165 } 166 } 167 for dest := range baseVolumes { 168 if _, ok := baseMounts[dest]; ok { 169 return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest) 170 } 171 } 172 // Final step: maps to arrays 173 finalMounts := make([]spec.Mount, 0, len(baseMounts)) 174 for _, mount := range baseMounts { 175 if mount.Type == define.TypeBind { 176 absSrc, err := filepath.Abs(mount.Source) 177 if err != nil { 178 return nil, nil, nil, errors.Wrapf(err, "error getting absolute path of %s", mount.Source) 179 } 180 mount.Source = absSrc 181 } 182 finalMounts = append(finalMounts, mount) 183 } 184 finalVolumes := make([]*specgen.NamedVolume, 0, len(baseVolumes)) 185 for _, volume := range baseVolumes { 186 finalVolumes = append(finalVolumes, volume) 187 } 188 189 finalOverlays := make([]*specgen.OverlayVolume, 0, len(unifiedOverlays)) 190 for _, volume := range unifiedOverlays { 191 finalOverlays = append(finalOverlays, volume) 192 } 193 194 return finalMounts, finalVolumes, finalOverlays, nil 195 } 196 197 // Get image volumes from the given image 198 func getImageVolumes(ctx context.Context, img *libimage.Image, s *specgen.SpecGenerator) (map[string]spec.Mount, map[string]*specgen.NamedVolume, error) { 199 mounts := make(map[string]spec.Mount) 200 volumes := make(map[string]*specgen.NamedVolume) 201 202 mode := strings.ToLower(s.ImageVolumeMode) 203 204 // Image may be nil (rootfs in use), or image volume mode may be ignore. 205 if img == nil || mode == "ignore" { 206 return mounts, volumes, nil 207 } 208 209 inspect, err := img.Inspect(ctx, nil) 210 if err != nil { 211 return nil, nil, errors.Wrapf(err, "error inspecting image to get image volumes") 212 } 213 for volume := range inspect.Config.Volumes { 214 logrus.Debugf("Image has volume at %q", volume) 215 cleanDest := filepath.Clean(volume) 216 switch mode { 217 case "", "anonymous": 218 // Anonymous volumes have no name. 219 newVol := new(specgen.NamedVolume) 220 newVol.Dest = cleanDest 221 newVol.Options = []string{"rprivate", "rw", "nodev", "exec"} 222 volumes[cleanDest] = newVol 223 logrus.Debugf("Adding anonymous image volume at %q", cleanDest) 224 case "tmpfs": 225 mount := spec.Mount{ 226 Destination: cleanDest, 227 Source: define.TypeTmpfs, 228 Type: define.TypeTmpfs, 229 Options: []string{"rprivate", "rw", "nodev", "exec"}, 230 } 231 mounts[cleanDest] = mount 232 logrus.Debugf("Adding tmpfs image volume at %q", cleanDest) 233 } 234 } 235 236 return mounts, volumes, nil 237 } 238 239 func getVolumesFrom(volumesFrom []string, runtime *libpod.Runtime) (map[string]spec.Mount, map[string]*specgen.NamedVolume, error) { 240 finalMounts := make(map[string]spec.Mount) 241 finalNamedVolumes := make(map[string]*specgen.NamedVolume) 242 243 for _, volume := range volumesFrom { 244 var options []string 245 246 splitVol := strings.SplitN(volume, ":", 2) 247 if len(splitVol) == 2 { 248 splitOpts := strings.Split(splitVol[1], ",") 249 setRORW := false 250 setZ := false 251 for _, opt := range splitOpts { 252 switch opt { 253 case "z": 254 if setZ { 255 return nil, nil, errors.Errorf("cannot set :z more than once in mount options") 256 } 257 setZ = true 258 case "ro", "rw": 259 if setRORW { 260 return nil, nil, errors.Errorf("cannot set ro or rw options more than once") 261 } 262 setRORW = true 263 default: 264 return nil, nil, errors.Errorf("invalid option %q specified - volumes from another container can only use z,ro,rw options", opt) 265 } 266 } 267 options = splitOpts 268 } 269 270 ctr, err := runtime.LookupContainer(splitVol[0]) 271 if err != nil { 272 return nil, nil, errors.Wrapf(err, "error looking up container %q for volumes-from", splitVol[0]) 273 } 274 275 logrus.Debugf("Adding volumes from container %s", ctr.ID()) 276 277 // Look up the container's user volumes. This gets us the 278 // destinations of all mounts the user added to the container. 279 userVolumesArr := ctr.UserVolumes() 280 281 // We're going to need to access them a lot, so convert to a map 282 // to reduce looping. 283 // We'll also use the map to indicate if we missed any volumes along the way. 284 userVolumes := make(map[string]bool) 285 for _, dest := range userVolumesArr { 286 userVolumes[dest] = false 287 } 288 289 // Now we get the container's spec and loop through its volumes 290 // and append them in if we can find them. 291 spec := ctr.Spec() 292 if spec == nil { 293 return nil, nil, errors.Errorf("retrieving container %s spec for volumes-from", ctr.ID()) 294 } 295 for _, mnt := range spec.Mounts { 296 if mnt.Type != define.TypeBind { 297 continue 298 } 299 if _, exists := userVolumes[mnt.Destination]; exists { 300 userVolumes[mnt.Destination] = true 301 302 if len(options) != 0 { 303 mnt.Options = options 304 } 305 306 if _, ok := finalMounts[mnt.Destination]; ok { 307 logrus.Debugf("Overriding mount to %s with new mount from container %s", mnt.Destination, ctr.ID()) 308 } 309 finalMounts[mnt.Destination] = mnt 310 } 311 } 312 313 // We're done with the spec mounts. Add named volumes. 314 // Add these unconditionally - none of them are automatically 315 // part of the container, as some spec mounts are. 316 namedVolumes := ctr.NamedVolumes() 317 for _, namedVol := range namedVolumes { 318 if _, exists := userVolumes[namedVol.Dest]; exists { 319 userVolumes[namedVol.Dest] = true 320 } 321 322 if len(options) != 0 { 323 namedVol.Options = options 324 } 325 326 if _, ok := finalMounts[namedVol.Dest]; ok { 327 logrus.Debugf("Overriding named volume mount to %s with new named volume from container %s", namedVol.Dest, ctr.ID()) 328 } 329 if err = parse.ValidateVolumeCtrDir(namedVol.Dest); err != nil { 330 return nil, nil, err 331 } 332 333 cleanDest := filepath.Clean(namedVol.Dest) 334 newVol := new(specgen.NamedVolume) 335 newVol.Dest = cleanDest 336 newVol.Options = namedVol.Options 337 newVol.Name = namedVol.Name 338 339 finalNamedVolumes[namedVol.Dest] = newVol 340 } 341 342 // Check if we missed any volumes 343 for volDest, found := range userVolumes { 344 if !found { 345 logrus.Warnf("Unable to match volume %s from container %s for volumes-from", volDest, ctr.ID()) 346 } 347 } 348 } 349 350 return finalMounts, finalNamedVolumes, nil 351 } 352 353 // AddContainerInitBinary adds the init binary specified by path iff the 354 // container will run in a private PID namespace that is not shared with the 355 // host or another pre-existing container, where an init-like process is 356 // already running. 357 // This does *NOT* modify the container command - that must be done elsewhere. 358 func addContainerInitBinary(s *specgen.SpecGenerator, path string) (spec.Mount, error) { 359 mount := spec.Mount{ 360 Destination: define.ContainerInitPath, 361 Type: define.TypeBind, 362 Source: path, 363 Options: []string{define.TypeBind, "ro"}, 364 } 365 366 if path == "" { 367 return mount, fmt.Errorf("please specify a path to the container-init binary") 368 } 369 if !s.PidNS.IsPrivate() { 370 return mount, fmt.Errorf("cannot add init binary as PID 1 (PID namespace isn't private)") 371 } 372 if s.Systemd == "always" { 373 return mount, fmt.Errorf("cannot use container-init binary with systemd=always") 374 } 375 if _, err := os.Stat(path); os.IsNotExist(err) { 376 return mount, errors.Wrap(err, "container-init binary not found on the host") 377 } 378 return mount, nil 379 } 380 381 // Supersede existing mounts in the spec with new, user-specified mounts. 382 // TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by 383 // one mount, and we already have /tmp/a and /tmp/b, should we remove 384 // the /tmp/a and /tmp/b mounts in favor of the more general /tmp? 385 func SupersedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount { 386 if len(mounts) > 0 { 387 // If we have overlappings mounts, remove them from the spec in favor of 388 // the user-added volume mounts 389 destinations := make(map[string]bool) 390 for _, mount := range mounts { 391 destinations[path.Clean(mount.Destination)] = true 392 } 393 // Copy all mounts from spec to defaultMounts, except for 394 // - mounts overridden by a user supplied mount; 395 // - all mounts under /dev if a user supplied /dev is present; 396 mountDev := destinations["/dev"] 397 for _, mount := range configMount { 398 if _, ok := destinations[path.Clean(mount.Destination)]; !ok { 399 if mountDev && strings.HasPrefix(mount.Destination, "/dev/") { 400 // filter out everything under /dev if /dev is user-mounted 401 continue 402 } 403 404 logrus.Debugf("Adding mount %s", mount.Destination) 405 mounts = append(mounts, mount) 406 } 407 } 408 return mounts 409 } 410 return configMount 411 } 412 413 func InitFSMounts(mounts []spec.Mount) error { 414 for i, m := range mounts { 415 switch { 416 case m.Type == define.TypeBind: 417 opts, err := util.ProcessOptions(m.Options, false, m.Source) 418 if err != nil { 419 return err 420 } 421 mounts[i].Options = opts 422 case m.Type == define.TypeTmpfs && filepath.Clean(m.Destination) != "/dev": 423 opts, err := util.ProcessOptions(m.Options, true, "") 424 if err != nil { 425 return err 426 } 427 mounts[i].Options = opts 428 } 429 } 430 return nil 431 }