//go:build !remote && (linux || freebsd) package libpod import ( "context" "errors" "fmt" "strings" "go.podman.io/podman/v6/libpod/define" "go.podman.io/podman/v6/libpod/events" "go.podman.io/podman/v6/pkg/domain/entities/reports" ) // Contains the public Runtime API for volumes // A VolumeCreateOption is a functional option which alters the Volume created by // NewVolume type VolumeCreateOption func(*Volume) error // VolumeFilter is a function to determine whether a volume is included in command // output. Volumes to be outputted are tested using the function. a true return will // include the volume, a false return will exclude it. type VolumeFilter func(*Volume) bool // RemoveVolume removes a volumes func (r *Runtime) RemoveVolume(ctx context.Context, v *Volume, force bool, timeout *uint) error { if !r.valid { return define.ErrRuntimeStopped } return r.removeVolume(ctx, v, force, timeout, false) } // GetVolume retrieves a volume given its full name. func (r *Runtime) GetVolume(name string) (*Volume, error) { if !r.valid { return nil, define.ErrRuntimeStopped } vol, err := r.state.Volume(name) if err != nil { return nil, err } return vol, nil } // LookupVolume retrieves a volume by unambiguous partial name. func (r *Runtime) LookupVolume(name string) (*Volume, error) { if !r.valid { return nil, define.ErrRuntimeStopped } vol, err := r.state.LookupVolume(name) if err != nil { return nil, err } return vol, nil } // HasVolume checks to see if a volume with the given name exists func (r *Runtime) HasVolume(name string) (bool, error) { if !r.valid { return false, define.ErrRuntimeStopped } return r.state.HasVolume(name) } // Volumes retrieves all volumes // Filters can be provided which will determine which volumes are included in the // output. If multiple filters are used, a volume will be returned if // all of the filters are matched func (r *Runtime) Volumes(filters ...VolumeFilter) ([]*Volume, error) { if !r.valid { return nil, define.ErrRuntimeStopped } vols, err := r.state.AllVolumes() if err != nil { return nil, err } if len(filters) == 0 { return vols, nil } volsFiltered := make([]*Volume, 0, len(vols)) for _, vol := range vols { include := true for _, filter := range filters { include = include && filter(vol) } if include { volsFiltered = append(volsFiltered, vol) } } return volsFiltered, nil } // GetAllVolumes retrieves all the volumes func (r *Runtime) GetAllVolumes() ([]*Volume, error) { if !r.valid { return nil, define.ErrRuntimeStopped } return r.state.AllVolumes() } // RenameVolume renames the given volume to a new name. // The volume must not be in use by any containers, and must use the local // driver. func (r *Runtime) RenameVolume(_ context.Context, vol *Volume, newName string) (*Volume, error) { if !r.valid { return nil, define.ErrRuntimeStopped } vol.lock.Lock() defer vol.lock.Unlock() if err := vol.update(); err != nil { return nil, err } if newName == "" || !define.NameRegex.MatchString(newName) { return nil, define.RegexError } if vol.Name() == newName { return vol, nil } // Only local-driver volumes can be renamed driver := vol.Driver() if driver != "" && driver != define.VolumeDriverLocal { return nil, fmt.Errorf("renaming volume %s: rename is not supported for volumes using driver %q: %w", vol.Name(), driver, define.ErrInvalidArg) } // Refuse rename if the volume is currently mounted if vol.state.MountCount > 0 { return nil, fmt.Errorf("renaming volume %s: volume is currently mounted: %w", vol.Name(), define.ErrVolumeBeingUsed) } // Refuse rename if the volume is in use by any container ctrs, err := r.state.VolumeInUse(vol) if err != nil { return nil, fmt.Errorf("checking if volume %s is in use: %w", vol.Name(), err) } if len(ctrs) > 0 { return nil, fmt.Errorf("volume %s is being used by the following container(s): %s: %w", vol.Name(), strings.Join(ctrs, ", "), define.ErrVolumeBeingUsed) } oldName := vol.config.Name config := *vol.config config.Name = newName config.MountPoint = r.volumeDataPath(newName) config.IsAnon = false if err := r.state.RenameVolume(vol, &config); err != nil { return nil, fmt.Errorf("renaming volume %s: %w", oldName, err) } vol.config = &config vol.newVolumeEvent(events.Rename) return vol, nil } // PruneVolumes removes unused volumes from the system func (r *Runtime) PruneVolumes(ctx context.Context, filterFuncs []VolumeFilter, dryRun bool) ([]*reports.PruneReport, error) { preports := make([]*reports.PruneReport, 0) vols, err := r.Volumes(filterFuncs...) if err != nil { return nil, err } for _, vol := range vols { dangling, err := vol.IsDangling() if err != nil { preports = append(preports, &reports.PruneReport{ Id: vol.Name(), Err: err, }) } if !dangling { continue } report := new(reports.PruneReport) volSize, err := vol.Size() if err != nil { volSize = 0 } report.Size = volSize report.Id = vol.Name() if !dryRun { var timeout *uint if err := r.RemoveVolume(ctx, vol, false, timeout); err != nil { if !errors.Is(err, define.ErrVolumeBeingUsed) && !errors.Is(err, define.ErrVolumeRemoved) { report.Err = err } else { // We didn't remove the volume for some reason continue } } else { vol.newVolumeEvent(events.Prune) } } preports = append(preports, report) } return preports, nil }