summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
Diffstat (limited to 'libpod')
-rw-r--r--libpod/boltdb_state.go2
-rw-r--r--libpod/boltdb_state_linux.go1
-rw-r--r--libpod/common/common.go6
-rw-r--r--libpod/container_api.go6
-rw-r--r--libpod/container_config.go4
-rw-r--r--libpod/container_copy_linux.go1
-rw-r--r--libpod/container_exec.go56
-rw-r--r--libpod/container_inspect.go1
-rw-r--r--libpod/container_internal.go12
-rw-r--r--libpod/container_internal_linux.go95
-rw-r--r--libpod/container_linux.go1
-rw-r--r--libpod/container_log_linux.go7
-rw-r--r--libpod/container_log_unsupported.go3
-rw-r--r--libpod/container_stat_linux.go1
-rw-r--r--libpod/container_top_linux.go1
-rw-r--r--libpod/define/container_inspect.go4
-rw-r--r--libpod/define/containerstate.go1
-rw-r--r--libpod/events/events_unsupported.go1
-rw-r--r--libpod/events/journal_linux.go1
-rw-r--r--libpod/events/journal_unsupported.go1
-rw-r--r--libpod/events/logfile.go5
-rw-r--r--libpod/kube.go8
-rw-r--r--libpod/linkmode/linkmode_dynamic.go1
-rw-r--r--libpod/linkmode/linkmode_static.go1
-rw-r--r--libpod/lock/shm/shm_lock.go1
-rw-r--r--libpod/lock/shm/shm_lock_nocgo.go1
-rw-r--r--libpod/lock/shm/shm_lock_test.go1
-rw-r--r--libpod/lock/shm_lock_manager_linux.go1
-rw-r--r--libpod/lock/shm_lock_manager_unsupported.go1
-rw-r--r--libpod/mounts_linux.go1
-rw-r--r--libpod/networking_linux.go91
-rw-r--r--libpod/networking_slirp4netns.go41
-rw-r--r--libpod/oci_attach_linux.go3
-rw-r--r--libpod/oci_conmon_linux.go12
-rw-r--r--libpod/options.go15
-rw-r--r--libpod/pod.go9
-rw-r--r--libpod/pod_top_linux.go1
-rw-r--r--libpod/runtime.go4
-rw-r--r--libpod/runtime_migrate.go1
-rw-r--r--libpod/runtime_pod_linux.go74
-rw-r--r--libpod/runtime_volume_linux.go1
-rw-r--r--libpod/stats.go23
-rw-r--r--libpod/util_linux.go1
-rw-r--r--libpod/volume.go13
-rw-r--r--libpod/volume_internal_linux.go1
45 files changed, 409 insertions, 107 deletions
diff --git a/libpod/boltdb_state.go b/libpod/boltdb_state.go
index 6389431ab..9745121c7 100644
--- a/libpod/boltdb_state.go
+++ b/libpod/boltdb_state.go
@@ -366,7 +366,7 @@ func (s *BoltState) GetDBConfig() (*DBConfig, error) {
err = db.View(func(tx *bolt.Tx) error {
configBucket, err := getRuntimeConfigBucket(tx)
if err != nil {
- return nil
+ return err
}
// Some of these may be nil
diff --git a/libpod/boltdb_state_linux.go b/libpod/boltdb_state_linux.go
index 63ce9784e..8bb10fb63 100644
--- a/libpod/boltdb_state_linux.go
+++ b/libpod/boltdb_state_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/common/common.go b/libpod/common/common.go
index 93a736af2..34cabeadc 100644
--- a/libpod/common/common.go
+++ b/libpod/common/common.go
@@ -1,16 +1,16 @@
package common
-// IsTrue determines whether the given string equals "true"
+// IsTrue determines whether the given string equals "true".
func IsTrue(str string) bool {
return str == "true"
}
-// IsFalse determines whether the given string equals "false"
+// IsFalse determines whether the given string equals "false".
func IsFalse(str string) bool {
return str == "false"
}
-// IsValidBool determines whether the given string equals "true" or "false"
+// IsValidBool determines whether the given string equals "true" or "false".
func IsValidBool(str string) bool {
return IsTrue(str) || IsFalse(str)
}
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 03b3dcc04..0b6139335 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -921,7 +921,11 @@ func (c *Container) Stat(ctx context.Context, containerPath string) (*define.Fil
if err != nil {
return nil, err
}
- defer c.unmount(false)
+ defer func() {
+ if err := c.unmount(false); err != nil {
+ logrus.Errorf("Unmounting container %s: %v", c.ID(), err)
+ }
+ }()
}
info, _, _, err := c.stat(ctx, mountPoint, containerPath)
diff --git a/libpod/container_config.go b/libpod/container_config.go
index e56f1342a..0d9cd5723 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -165,6 +165,10 @@ type ContainerRootFSConfig struct {
Volatile bool `json:"volatile,omitempty"`
// Passwd allows to user to override podman's passwd/group file setup
Passwd *bool `json:"passwd,omitempty"`
+ // ChrootDirs is an additional set of directories that need to be
+ // treated as root directories. Standard bind mounts will be mounted
+ // into paths relative to these directories.
+ ChrootDirs []string `json:"chroot_directories,omitempty"`
}
// ContainerSecurityConfig is an embedded sub-config providing security configuration
diff --git a/libpod/container_copy_linux.go b/libpod/container_copy_linux.go
index d16d635b7..38927d691 100644
--- a/libpod/container_copy_linux.go
+++ b/libpod/container_copy_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/container_exec.go b/libpod/container_exec.go
index d1c190905..140267f28 100644
--- a/libpod/container_exec.go
+++ b/libpod/container_exec.go
@@ -341,22 +341,60 @@ func (c *Container) ExecStartAndAttach(sessionID string, streams *define.AttachS
}
lastErr = tmpErr
- exitCode, err := c.readExecExitCode(session.ID())
- if err != nil {
+ exitCode, exitCodeErr := c.readExecExitCode(session.ID())
+
+ // Lock again.
+ // Important: we must lock and sync *before* the above error is handled.
+ // We need info from the database to handle the error.
+ if !c.batched {
+ c.lock.Lock()
+ }
+ // We can't reuse the old exec session (things may have changed from
+ // other use, the container was unlocked).
+ // So re-sync and get a fresh copy.
+ // If we can't do this, no point in continuing, any attempt to save
+ // would write garbage to the DB.
+ if err := c.syncContainer(); err != nil {
+ if errors.Is(err, define.ErrNoSuchCtr) || errors.Is(err, define.ErrCtrRemoved) {
+ // We can't save status, but since the container has
+ // been entirely removed, we don't have to; exit cleanly
+ return lastErr
+ }
if lastErr != nil {
logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr)
}
- lastErr = err
- }
+ return errors.Wrapf(err, "error syncing container %s state to update exec session %s", c.ID(), sessionID)
+ }
+
+ // Now handle the error from readExecExitCode above.
+ if exitCodeErr != nil {
+ newSess, ok := c.state.ExecSessions[sessionID]
+ if !ok {
+ // The exec session was removed entirely, probably by
+ // the cleanup process. When it did so, it should have
+ // written an event with the exit code.
+ // Given that, there's nothing more we can do.
+ logrus.Infof("Container %s exec session %s already removed", c.ID(), session.ID())
+ return lastErr
+ }
- logrus.Debugf("Container %s exec session %s completed with exit code %d", c.ID(), session.ID(), exitCode)
+ if newSess.State == define.ExecStateStopped {
+ // Exec session already cleaned up.
+ // Exit code should be recorded, so it's OK if we were
+ // not able to read it.
+ logrus.Infof("Container %s exec session %s already cleaned up", c.ID(), session.ID())
+ return lastErr
+ }
- // Lock again
- if !c.batched {
- c.lock.Lock()
+ if lastErr != nil {
+ logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr)
+ }
+ lastErr = exitCodeErr
}
- if err := writeExecExitCode(c, session.ID(), exitCode); err != nil {
+ logrus.Debugf("Container %s exec session %s completed with exit code %d", c.ID(), session.ID(), exitCode)
+
+ if err := justWriteExecExitCode(c, session.ID(), exitCode); err != nil {
if lastErr != nil {
logrus.Errorf("Container %s exec session %s error: %v", c.ID(), session.ID(), lastErr)
}
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index 3df6203e3..5fb32bd90 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -411,6 +411,7 @@ func (c *Container) generateInspectContainerConfig(spec *spec.Spec) *define.Insp
}
ctrConfig.Passwd = c.config.Passwd
+ ctrConfig.ChrootDirs = append(ctrConfig.ChrootDirs, c.config.ChrootDirs...)
return ctrConfig
}
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index 3c21cade8..b7362e7fb 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -1087,13 +1087,6 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error {
// With the spec complete, do an OCI create
if _, err = c.ociRuntime.CreateContainer(c, nil); err != nil {
- // Fedora 31 is carrying a patch to display improved error
- // messages to better handle the V2 transition. This is NOT
- // upstream in any OCI runtime.
- // TODO: Remove once runc supports cgroupsv2
- if strings.Contains(err.Error(), "this version of runc doesn't work on cgroups v2") {
- logrus.Errorf("Oci runtime %q does not support Cgroups V2: use system migrate to mitigate", c.ociRuntime.Name())
- }
return err
}
@@ -1268,7 +1261,10 @@ func (c *Container) start() error {
}
}
- if c.config.HealthCheckConfig != nil {
+ // Check if healthcheck is not nil and --no-healthcheck option is not set.
+ // If --no-healthcheck is set Test will be always set to `[NONE]` so no need
+ // to update status in such case.
+ if c.config.HealthCheckConfig != nil && !(len(c.config.HealthCheckConfig.Test) == 1 && c.config.HealthCheckConfig.Test[0] == "NONE") {
if err := c.updateHealthStatus(define.HealthCheckStarting); err != nil {
logrus.Error(err)
}
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index cef9e2c04..4d6922d73 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -173,6 +173,57 @@ func (c *Container) prepare() error {
return nil
}
+// isWorkDirSymlink returns true if resolved workdir is symlink or a chain of symlinks,
+// and final resolved target is present either on volume, mount or inside of container
+// otherwise it returns false. Following function is meant for internal use only and
+// can change at any point of time.
+func (c *Container) isWorkDirSymlink(resolvedPath string) bool {
+ // We cannot create workdir since explicit --workdir is
+ // set in config but workdir could also be a symlink.
+ // If its a symlink lets check if resolved link is present
+ // on the container or not.
+
+ // If we can resolve symlink and resolved link is present on the container
+ // then return nil cause its a valid use-case.
+
+ maxSymLinks := 0
+ for {
+ // Linux only supports a chain of 40 links.
+ // Reference: https://github.com/torvalds/linux/blob/master/include/linux/namei.h#L13
+ if maxSymLinks > 40 {
+ break
+ }
+ resolvedSymlink, err := os.Readlink(resolvedPath)
+ if err != nil {
+ // End sym-link resolution loop.
+ break
+ }
+ if resolvedSymlink != "" {
+ _, resolvedSymlinkWorkdir, err := c.resolvePath(c.state.Mountpoint, resolvedSymlink)
+ if isPathOnVolume(c, resolvedSymlinkWorkdir) || isPathOnBindMount(c, resolvedSymlinkWorkdir) {
+ // Resolved symlink exists on external volume or mount
+ return true
+ }
+ if err != nil {
+ // Could not resolve path so end sym-link resolution loop.
+ break
+ }
+ if resolvedSymlinkWorkdir != "" {
+ resolvedPath = resolvedSymlinkWorkdir
+ _, err := os.Stat(resolvedSymlinkWorkdir)
+ if err == nil {
+ // Symlink resolved successfully and resolved path exists on container,
+ // this is a valid use-case so return nil.
+ logrus.Debugf("Workdir is a symlink with target to %q and resolved symlink exists on container", resolvedSymlink)
+ return true
+ }
+ }
+ }
+ maxSymLinks++
+ }
+ return false
+}
+
// resolveWorkDir resolves the container's workdir and, depending on the
// configuration, will create it, or error out if it does not exist.
// Note that the container must be mounted before.
@@ -205,6 +256,11 @@ func (c *Container) resolveWorkDir() error {
// the path exists on the container.
if err != nil {
if os.IsNotExist(err) {
+ // If resolved Workdir path gets marked as a valid symlink,
+ // return nil cause this is valid use-case.
+ if c.isWorkDirSymlink(resolvedWorkdir) {
+ return nil
+ }
return errors.Errorf("workdir %q does not exist on container %s", workdir, c.ID())
}
// This might be a serious error (e.g., permission), so
@@ -1755,6 +1811,17 @@ func (c *Container) getRootNetNsDepCtr() (depCtr *Container, err error) {
return depCtr, nil
}
+// Ensure standard bind mounts are mounted into all root directories (including chroot directories)
+func (c *Container) mountIntoRootDirs(mountName string, mountPath string) error {
+ c.state.BindMounts[mountName] = mountPath
+
+ for _, chrootDir := range c.config.ChrootDirs {
+ c.state.BindMounts[filepath.Join(chrootDir, mountName)] = mountPath
+ }
+
+ return nil
+}
+
// Make standard bind mounts to include in the container
func (c *Container) makeBindMounts() error {
if err := os.Chown(c.state.RunDir, c.RootUID(), c.RootGID()); err != nil {
@@ -1808,7 +1875,11 @@ func (c *Container) makeBindMounts() error {
// If it doesn't, don't copy them
resolvPath, exists := bindMounts["/etc/resolv.conf"]
if !c.config.UseImageResolvConf && exists {
- c.state.BindMounts["/etc/resolv.conf"] = resolvPath
+ err := c.mountIntoRootDirs("/etc/resolv.conf", resolvPath)
+
+ if err != nil {
+ return errors.Wrapf(err, "error assigning mounts to container %s", c.ID())
+ }
}
// check if dependency container has an /etc/hosts file.
@@ -1828,7 +1899,11 @@ func (c *Container) makeBindMounts() error {
depCtr.lock.Unlock()
// finally, save it in the new container
- c.state.BindMounts["/etc/hosts"] = hostsPath
+ err := c.mountIntoRootDirs("/etc/hosts", hostsPath)
+
+ if err != nil {
+ return errors.Wrapf(err, "error assigning mounts to container %s", c.ID())
+ }
}
if !hasCurrentUserMapped(c) {
@@ -1845,7 +1920,11 @@ func (c *Container) makeBindMounts() error {
if err != nil {
return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID())
}
- c.state.BindMounts["/etc/resolv.conf"] = newResolv
+ err = c.mountIntoRootDirs("/etc/resolv.conf", newResolv)
+
+ if err != nil {
+ return errors.Wrapf(err, "error assigning mounts to container %s", c.ID())
+ }
}
if !c.config.UseImageHosts {
@@ -2273,7 +2352,11 @@ func (c *Container) updateHosts(path string) error {
if err != nil {
return err
}
- c.state.BindMounts["/etc/hosts"] = newHosts
+
+ if err = c.mountIntoRootDirs("/etc/hosts", newHosts); err != nil {
+ return err
+ }
+
return nil
}
@@ -2504,7 +2587,7 @@ func (c *Container) generateUserGroupEntry(addedGID int) (string, int, error) {
gid, err := strconv.ParseUint(group, 10, 32)
if err != nil {
- return "", 0, nil
+ return "", 0, nil // nolint: nilerr
}
if addedGID != 0 && addedGID == int(gid) {
@@ -2657,7 +2740,7 @@ func (c *Container) generateUserPasswdEntry(addedUID int) (string, int, int, err
// If a non numeric User, then don't generate passwd
uid, err := strconv.ParseUint(userspec, 10, 32)
if err != nil {
- return "", 0, 0, nil
+ return "", 0, 0, nil // nolint: nilerr
}
if addedUID != 0 && int(uid) == addedUID {
diff --git a/libpod/container_linux.go b/libpod/container_linux.go
index c445fb8af..8b517e69f 100644
--- a/libpod/container_linux.go
+++ b/libpod/container_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/container_log_linux.go b/libpod/container_log_linux.go
index 6150973ca..8ae8ff2c0 100644
--- a/libpod/container_log_linux.go
+++ b/libpod/container_log_linux.go
@@ -1,5 +1,5 @@
-//+build linux
-//+build systemd
+//go:build linux && systemd
+// +build linux,systemd
package libpod
@@ -235,6 +235,9 @@ func (c *Container) readFromJournal(ctx context.Context, options *logs.LogOption
logrus.Errorf("Failed parse log line: %v", err)
return
}
+ if options.UseName {
+ logLine.CName = c.Name()
+ }
if doTail {
tailQueue = append(tailQueue, logLine)
continue
diff --git a/libpod/container_log_unsupported.go b/libpod/container_log_unsupported.go
index f9ca26966..4f50f9f4c 100644
--- a/libpod/container_log_unsupported.go
+++ b/libpod/container_log_unsupported.go
@@ -1,4 +1,5 @@
-//+build !linux !systemd
+//go:build !linux || !systemd
+// +build !linux !systemd
package libpod
diff --git a/libpod/container_stat_linux.go b/libpod/container_stat_linux.go
index d90684197..84ab984e0 100644
--- a/libpod/container_stat_linux.go
+++ b/libpod/container_stat_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/container_top_linux.go b/libpod/container_top_linux.go
index 41300a708..9b3dbc873 100644
--- a/libpod/container_top_linux.go
+++ b/libpod/container_top_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 804b2b143..ae2ce9724 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -75,6 +75,10 @@ type InspectContainerConfig struct {
StopTimeout uint `json:"StopTimeout"`
// Passwd determines whether or not podman can add entries to /etc/passwd and /etc/group
Passwd *bool `json:"Passwd,omitempty"`
+ // ChrootDirs is an additional set of directories that need to be
+ // treated as root directories. Standard bind mounts will be mounted
+ // into paths relative to these directories.
+ ChrootDirs []string `json:"ChrootDirs,omitempty"`
}
// InspectRestartPolicy holds information about the container's restart policy.
diff --git a/libpod/define/containerstate.go b/libpod/define/containerstate.go
index 23ba1f451..9ad3aec08 100644
--- a/libpod/define/containerstate.go
+++ b/libpod/define/containerstate.go
@@ -138,7 +138,6 @@ type ContainerStats struct {
CPU float64
CPUNano uint64
CPUSystemNano uint64
- DataPoints int64
SystemNano uint64
MemUsage uint64
MemLimit uint64
diff --git a/libpod/events/events_unsupported.go b/libpod/events/events_unsupported.go
index 5b32a1b4b..25c175524 100644
--- a/libpod/events/events_unsupported.go
+++ b/libpod/events/events_unsupported.go
@@ -1,3 +1,4 @@
+//go:build !linux
// +build !linux
package events
diff --git a/libpod/events/journal_linux.go b/libpod/events/journal_linux.go
index cc63df120..866042a4c 100644
--- a/libpod/events/journal_linux.go
+++ b/libpod/events/journal_linux.go
@@ -1,3 +1,4 @@
+//go:build systemd
// +build systemd
package events
diff --git a/libpod/events/journal_unsupported.go b/libpod/events/journal_unsupported.go
index 004efdab2..6ed39792b 100644
--- a/libpod/events/journal_unsupported.go
+++ b/libpod/events/journal_unsupported.go
@@ -1,3 +1,4 @@
+//go:build !systemd
// +build !systemd
package events
diff --git a/libpod/events/logfile.go b/libpod/events/logfile.go
index be2aaacca..76173cde9 100644
--- a/libpod/events/logfile.go
+++ b/libpod/events/logfile.go
@@ -9,6 +9,7 @@ import (
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/storage/pkg/lockfile"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
)
// EventLogFile is the structure for event writing to a logfile. It contains the eventer
@@ -59,7 +60,9 @@ func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
}
go func() {
time.Sleep(time.Until(untilTime))
- t.Stop()
+ if err := t.Stop(); err != nil {
+ logrus.Errorf("Stopping logger: %v", err)
+ }
}()
}
funcDone := make(chan bool)
diff --git a/libpod/kube.go b/libpod/kube.go
index d68d46415..a193df2cb 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -15,6 +15,10 @@ import (
"github.com/containers/common/pkg/config"
"github.com/containers/podman/v4/libpod/define"
"github.com/containers/podman/v4/pkg/env"
+ v1 "github.com/containers/podman/v4/pkg/k8s.io/api/core/v1"
+ "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/api/resource"
+ v12 "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/apis/meta/v1"
+ "github.com/containers/podman/v4/pkg/k8s.io/apimachinery/pkg/util/intstr"
"github.com/containers/podman/v4/pkg/lookup"
"github.com/containers/podman/v4/pkg/namespaces"
"github.com/containers/podman/v4/pkg/specgen"
@@ -23,10 +27,6 @@ import (
"github.com/opencontainers/runtime-tools/generate"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
- v1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/api/resource"
- v12 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/util/intstr"
)
// GenerateForKube takes a slice of libpod containers and generates
diff --git a/libpod/linkmode/linkmode_dynamic.go b/libpod/linkmode/linkmode_dynamic.go
index 6d51d60e0..f020fa53e 100644
--- a/libpod/linkmode/linkmode_dynamic.go
+++ b/libpod/linkmode/linkmode_dynamic.go
@@ -1,3 +1,4 @@
+//go:build !static
// +build !static
package linkmode
diff --git a/libpod/linkmode/linkmode_static.go b/libpod/linkmode/linkmode_static.go
index 2db083f4a..b181ad285 100644
--- a/libpod/linkmode/linkmode_static.go
+++ b/libpod/linkmode/linkmode_static.go
@@ -1,3 +1,4 @@
+//go:build static
// +build static
package linkmode
diff --git a/libpod/lock/shm/shm_lock.go b/libpod/lock/shm/shm_lock.go
index fea02a619..c7f4d1bc5 100644
--- a/libpod/lock/shm/shm_lock.go
+++ b/libpod/lock/shm/shm_lock.go
@@ -1,3 +1,4 @@
+//go:build linux && cgo
// +build linux,cgo
package shm
diff --git a/libpod/lock/shm/shm_lock_nocgo.go b/libpod/lock/shm/shm_lock_nocgo.go
index 627344d9c..31fc02223 100644
--- a/libpod/lock/shm/shm_lock_nocgo.go
+++ b/libpod/lock/shm/shm_lock_nocgo.go
@@ -1,3 +1,4 @@
+//go:build linux && !cgo
// +build linux,!cgo
package shm
diff --git a/libpod/lock/shm/shm_lock_test.go b/libpod/lock/shm/shm_lock_test.go
index cb83c7c2c..8dfc849d6 100644
--- a/libpod/lock/shm/shm_lock_test.go
+++ b/libpod/lock/shm/shm_lock_test.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package shm
diff --git a/libpod/lock/shm_lock_manager_linux.go b/libpod/lock/shm_lock_manager_linux.go
index 8f3b6df7f..3076cd864 100644
--- a/libpod/lock/shm_lock_manager_linux.go
+++ b/libpod/lock/shm_lock_manager_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package lock
diff --git a/libpod/lock/shm_lock_manager_unsupported.go b/libpod/lock/shm_lock_manager_unsupported.go
index 1d6e3fcbd..d578359ab 100644
--- a/libpod/lock/shm_lock_manager_unsupported.go
+++ b/libpod/lock/shm_lock_manager_unsupported.go
@@ -1,3 +1,4 @@
+//go:build !linux
// +build !linux
package lock
diff --git a/libpod/mounts_linux.go b/libpod/mounts_linux.go
index e6aa09eac..f6945b3a3 100644
--- a/libpod/mounts_linux.go
+++ b/libpod/mounts_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go
index 29b9941fe..20c8059a5 100644
--- a/libpod/networking_linux.go
+++ b/libpod/networking_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
@@ -30,6 +31,7 @@ import (
"github.com/containers/podman/v4/pkg/util"
"github.com/containers/podman/v4/utils"
"github.com/containers/storage/pkg/lockfile"
+ spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -990,8 +992,20 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
return nil, err
}
- // We can't do more if the network is down.
if c.state.NetNS == nil {
+ if networkNSPath := c.joinedNetworkNSPath(); networkNSPath != "" {
+ if result, err := c.inspectJoinedNetworkNS(networkNSPath); err == nil {
+ if basicConfig, err := resultToBasicNetworkConfig(result); err == nil {
+ // fallback to dummy configuration
+ settings.InspectBasicNetworkConfig = basicConfig
+ return settings, nil
+ }
+ }
+ // do not propagate error inspecting a joined network ns
+ logrus.Errorf("Error inspecting network namespace: %s of container %s: %v", networkNSPath, c.ID(), err)
+ }
+ // We can't do more if the network is down.
+
// We still want to make dummy configurations for each CNI net
// the container joined.
if len(networks) > 0 {
@@ -1065,11 +1079,84 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
return settings, nil
}
+func (c *Container) joinedNetworkNSPath() string {
+ for _, namespace := range c.config.Spec.Linux.Namespaces {
+ if namespace.Type == spec.NetworkNamespace {
+ return namespace.Path
+ }
+ }
+ return ""
+}
+
+func (c *Container) inspectJoinedNetworkNS(networkns string) (q types.StatusBlock, retErr error) {
+ var result types.StatusBlock
+ err := ns.WithNetNSPath(networkns, func(_ ns.NetNS) error {
+ ifaces, err := net.Interfaces()
+ if err != nil {
+ return err
+ }
+ routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
+ if err != nil {
+ return err
+ }
+ var gateway net.IP
+ for _, route := range routes {
+ // default gateway
+ if route.Dst == nil {
+ gateway = route.Gw
+ }
+ }
+ result.Interfaces = make(map[string]types.NetInterface)
+ for _, iface := range ifaces {
+ if iface.Flags&net.FlagLoopback != 0 {
+ continue
+ }
+ addrs, err := iface.Addrs()
+ if err != nil {
+ continue
+ }
+ if len(addrs) == 0 {
+ continue
+ }
+ subnets := make([]types.NetAddress, 0, len(addrs))
+ for _, address := range addrs {
+ if ipnet, ok := address.(*net.IPNet); ok {
+ if ipnet.IP.IsLinkLocalMulticast() || ipnet.IP.IsLinkLocalUnicast() {
+ continue
+ }
+ subnet := types.NetAddress{
+ IPNet: types.IPNet{
+ IPNet: *ipnet,
+ },
+ }
+ if ipnet.Contains(gateway) {
+ subnet.Gateway = gateway
+ }
+ subnets = append(subnets, subnet)
+ }
+ }
+ result.Interfaces[iface.Name] = types.NetInterface{
+ Subnets: subnets,
+ MacAddress: types.HardwareAddr(iface.HardwareAddr),
+ }
+ }
+ return nil
+ })
+ return result, err
+}
+
// resultToBasicNetworkConfig produces an InspectBasicNetworkConfig from a CNI
// result
func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNetworkConfig, error) {
config := define.InspectBasicNetworkConfig{}
- for _, netInt := range result.Interfaces {
+ interfaceNames := make([]string, 0, len(result.Interfaces))
+ for interfaceName := range result.Interfaces {
+ interfaceNames = append(interfaceNames, interfaceName)
+ }
+ // ensure consistent inspect results by sorting
+ sort.Strings(interfaceNames)
+ for _, interfaceName := range interfaceNames {
+ netInt := result.Interfaces[interfaceName]
for _, netAddress := range netInt.Subnets {
size, _ := netAddress.IPNet.Mask.Size()
if netAddress.IPNet.IP.To4() != nil {
diff --git a/libpod/networking_slirp4netns.go b/libpod/networking_slirp4netns.go
index 690f0c1fa..a7a002657 100644
--- a/libpod/networking_slirp4netns.go
+++ b/libpod/networking_slirp4netns.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
@@ -13,6 +14,7 @@ import (
"path/filepath"
"strconv"
"strings"
+ "sync"
"syscall"
"time"
@@ -302,11 +304,15 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
cmd.Stdout = logFile
cmd.Stderr = logFile
- var slirpReadyChan (chan struct{})
-
+ var slirpReadyWg, netnsReadyWg *sync.WaitGroup
if netOptions.enableIPv6 {
- slirpReadyChan = make(chan struct{})
- defer close(slirpReadyChan)
+ // use two wait groups to make sure we set the sysctl before
+ // starting slirp and reset it only after slirp is ready
+ slirpReadyWg = &sync.WaitGroup{}
+ netnsReadyWg = &sync.WaitGroup{}
+ slirpReadyWg.Add(1)
+ netnsReadyWg.Add(1)
+
go func() {
err := ns.WithNetNSPath(netnsPath, func(_ ns.NetNS) error {
// Duplicate Address Detection slows the ipv6 setup down for 1-2 seconds.
@@ -318,23 +324,37 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
// is ready in case users rely on this sysctl.
orgValue, err := ioutil.ReadFile(ipv6ConfDefaultAcceptDadSysctl)
if err != nil {
+ netnsReadyWg.Done()
+ // on ipv6 disabled systems the sysctl does not exists
+ // so we should not error
+ if errors.Is(err, os.ErrNotExist) {
+ return nil
+ }
return err
}
err = ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, []byte("0"), 0644)
+ netnsReadyWg.Done()
if err != nil {
return err
}
- // wait for slirp to finish setup
- <-slirpReadyChan
+
+ // wait until slirp4nets is ready before reseting this value
+ slirpReadyWg.Wait()
return ioutil.WriteFile(ipv6ConfDefaultAcceptDadSysctl, orgValue, 0644)
})
if err != nil {
logrus.Warnf("failed to set net.ipv6.conf.default.accept_dad sysctl: %v", err)
}
}()
+
+ // wait until we set the sysctl
+ netnsReadyWg.Wait()
}
if err := cmd.Start(); err != nil {
+ if netOptions.enableIPv6 {
+ slirpReadyWg.Done()
+ }
return errors.Wrapf(err, "failed to start slirp4netns process")
}
defer func() {
@@ -344,11 +364,12 @@ func (r *Runtime) setupSlirp4netns(ctr *Container, netns ns.NetNS) error {
}
}()
- if err := waitForSync(syncR, cmd, logFile, 1*time.Second); err != nil {
- return err
+ err = waitForSync(syncR, cmd, logFile, 1*time.Second)
+ if netOptions.enableIPv6 {
+ slirpReadyWg.Done()
}
- if slirpReadyChan != nil {
- slirpReadyChan <- struct{}{}
+ if err != nil {
+ return err
}
// Set a default slirp subnet. Parsing a string with the net helper is easier than building the struct myself
diff --git a/libpod/oci_attach_linux.go b/libpod/oci_attach_linux.go
index 1ee664e81..b5eabec1f 100644
--- a/libpod/oci_attach_linux.go
+++ b/libpod/oci_attach_linux.go
@@ -1,4 +1,5 @@
-//+build linux
+//go:build linux
+// +build linux
package libpod
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index a328f7621..ba4079bed 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
@@ -749,7 +750,7 @@ func openControlFile(ctr *Container, parentDir string) (*os.File, error) {
for i := 0; i < 600; i++ {
controlFile, err := os.OpenFile(controlPath, unix.O_WRONLY|unix.O_NONBLOCK, 0)
if err == nil {
- return controlFile, err
+ return controlFile, nil
}
if !isRetryable(err) {
return nil, errors.Wrapf(err, "could not open ctl file for terminal resize for container %s", ctr.ID())
@@ -1014,7 +1015,8 @@ func (r *ConmonOCIRuntime) getLogTag(ctr *Container) (string, error) {
}
data, err := ctr.inspectLocked(false)
if err != nil {
- return "", nil
+ // FIXME: this error should probably be returned
+ return "", nil // nolint: nilerr
}
tmpl, err := template.New("container").Parse(logTag)
if err != nil {
@@ -1199,7 +1201,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
cmd.ExtraFiles = append(cmd.ExtraFiles, childSyncPipe, childStartPipe)
if r.reservePorts && !rootless.IsRootless() && !ctr.config.NetMode.IsSlirp4netns() {
- ports, err := bindPorts(ctr.config.PortMappings)
+ ports, err := bindPorts(ctr.convertPortMappings())
if err != nil {
return 0, err
}
@@ -1370,7 +1372,7 @@ func (r *ConmonOCIRuntime) sharedConmonArgs(ctr *Container, cuuid, bundlePath, p
case define.JSONLogging:
fallthrough
//lint:ignore ST1015 the default case has to be here
- default: //nolint-stylecheck
+ default: //nolint:stylecheck
// No case here should happen except JSONLogging, but keep this here in case the options are extended
logrus.Errorf("%s logging specified but not supported. Choosing k8s-file logging instead", ctr.LogDriver())
fallthrough
@@ -1595,7 +1597,7 @@ func readConmonPipeData(runtimeName string, pipe *os.File, ociLog string) (int,
ch <- syncStruct{si: si}
}()
- data := -1
+ data := -1 //nolint: wastedassign
select {
case ss := <-ch:
if ss.err != nil {
diff --git a/libpod/options.go b/libpod/options.go
index 1ee4e7322..2e5454393 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -2036,3 +2036,18 @@ func WithVolatile() CtrCreateOption {
return nil
}
}
+
+// WithChrootDirs is an additional set of directories that need to be
+// treated as root directories. Standard bind mounts will be mounted
+// into paths relative to these directories.
+func WithChrootDirs(dirs []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+
+ ctr.config.ChrootDirs = dirs
+
+ return nil
+ }
+}
diff --git a/libpod/pod.go b/libpod/pod.go
index 6273ff247..ed2d97b37 100644
--- a/libpod/pod.go
+++ b/libpod/pod.go
@@ -422,10 +422,6 @@ type PodContainerStats struct {
// GetPodStats returns the stats for each of its containers
func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerStats) (map[string]*define.ContainerStats, error) {
- var (
- ok bool
- prevStat *define.ContainerStats
- )
p.lock.Lock()
defer p.lock.Unlock()
@@ -438,10 +434,7 @@ func (p *Pod) GetPodStats(previousContainerStats map[string]*define.ContainerSta
}
newContainerStats := make(map[string]*define.ContainerStats)
for _, c := range containers {
- if prevStat, ok = previousContainerStats[c.ID()]; !ok {
- prevStat = &define.ContainerStats{}
- }
- newStats, err := c.GetContainerStats(prevStat)
+ newStats, err := c.GetContainerStats(previousContainerStats[c.ID()])
// If the container wasn't running, don't include it
// but also suppress the error
if err != nil && errors.Cause(err) != define.ErrCtrStateInvalid {
diff --git a/libpod/pod_top_linux.go b/libpod/pod_top_linux.go
index 43823a106..83a070807 100644
--- a/libpod/pod_top_linux.go
+++ b/libpod/pod_top_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/runtime.go b/libpod/runtime.go
index d19997709..07653217a 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -210,6 +210,10 @@ func newRuntimeFromConfig(ctx context.Context, conf *config.Config, options ...R
}
if err := shutdown.Register("libpod", func(sig os.Signal) error {
+ // For `systemctl stop podman.service` support, exit code should be 0
+ if sig == syscall.SIGTERM {
+ os.Exit(0)
+ }
os.Exit(1)
return nil
}); err != nil && errors.Cause(err) != shutdown.ErrHandlerExists {
diff --git a/libpod/runtime_migrate.go b/libpod/runtime_migrate.go
index 32fdc7b5d..fccd5bdee 100644
--- a/libpod/runtime_migrate.go
+++ b/libpod/runtime_migrate.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/runtime_pod_linux.go b/libpod/runtime_pod_linux.go
index 155ad5c2d..2bbccfdf6 100644
--- a/libpod/runtime_pod_linux.go
+++ b/libpod/runtime_pod_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
@@ -5,6 +6,7 @@ package libpod
import (
"context"
"fmt"
+ "os"
"path"
"path/filepath"
"strings"
@@ -59,50 +61,52 @@ func (r *Runtime) NewPod(ctx context.Context, p specgen.PodSpecGenerator, option
pod.valid = true
// Check Cgroup parent sanity, and set it if it was not set
- switch r.config.Engine.CgroupManager {
- case config.CgroupfsCgroupsManager:
- canUseCgroup := !rootless.IsRootless() || isRootlessCgroupSet(pod.config.CgroupParent)
- if canUseCgroup {
+ if r.config.Cgroups() != "disabled" {
+ switch r.config.Engine.CgroupManager {
+ case config.CgroupfsCgroupsManager:
+ canUseCgroup := !rootless.IsRootless() || isRootlessCgroupSet(pod.config.CgroupParent)
+ if canUseCgroup {
+ if pod.config.CgroupParent == "" {
+ pod.config.CgroupParent = CgroupfsDefaultCgroupParent
+ } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs")
+ }
+ // If we are set to use pod cgroups, set the cgroup parent that
+ // all containers in the pod will share
+ // No need to create it with cgroupfs - the first container to
+ // launch should do it for us
+ if pod.config.UsePodCgroup {
+ pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID())
+ if p.InfraContainerSpec != nil {
+ p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath
+ }
+ }
+ }
+ case config.SystemdCgroupsManager:
if pod.config.CgroupParent == "" {
- pod.config.CgroupParent = CgroupfsDefaultCgroupParent
- } else if strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") {
- return nil, errors.Wrapf(define.ErrInvalidArg, "systemd slice received as cgroup parent when using cgroupfs")
+ if rootless.IsRootless() {
+ pod.config.CgroupParent = SystemdDefaultRootlessCgroupParent
+ } else {
+ pod.config.CgroupParent = SystemdDefaultCgroupParent
+ }
+ } else if len(pod.config.CgroupParent) < 6 || !strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") {
+ return nil, errors.Wrapf(define.ErrInvalidArg, "did not receive systemd slice as cgroup parent when using systemd to manage cgroups")
}
// If we are set to use pod cgroups, set the cgroup parent that
// all containers in the pod will share
- // No need to create it with cgroupfs - the first container to
- // launch should do it for us
if pod.config.UsePodCgroup {
- pod.state.CgroupPath = filepath.Join(pod.config.CgroupParent, pod.ID())
+ cgroupPath, err := systemdSliceFromPath(pod.config.CgroupParent, fmt.Sprintf("libpod_pod_%s", pod.ID()))
+ if err != nil {
+ return nil, errors.Wrapf(err, "unable to create pod cgroup for pod %s", pod.ID())
+ }
+ pod.state.CgroupPath = cgroupPath
if p.InfraContainerSpec != nil {
p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath
}
}
+ default:
+ return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported Cgroup manager: %s - cannot validate cgroup parent", r.config.Engine.CgroupManager)
}
- case config.SystemdCgroupsManager:
- if pod.config.CgroupParent == "" {
- if rootless.IsRootless() {
- pod.config.CgroupParent = SystemdDefaultRootlessCgroupParent
- } else {
- pod.config.CgroupParent = SystemdDefaultCgroupParent
- }
- } else if len(pod.config.CgroupParent) < 6 || !strings.HasSuffix(path.Base(pod.config.CgroupParent), ".slice") {
- return nil, errors.Wrapf(define.ErrInvalidArg, "did not receive systemd slice as cgroup parent when using systemd to manage cgroups")
- }
- // If we are set to use pod cgroups, set the cgroup parent that
- // all containers in the pod will share
- if pod.config.UsePodCgroup {
- cgroupPath, err := systemdSliceFromPath(pod.config.CgroupParent, fmt.Sprintf("libpod_pod_%s", pod.ID()))
- if err != nil {
- return nil, errors.Wrapf(err, "unable to create pod cgroup for pod %s", pod.ID())
- }
- pod.state.CgroupPath = cgroupPath
- if p.InfraContainerSpec != nil {
- p.InfraContainerSpec.CgroupParent = pod.state.CgroupPath
- }
- }
- default:
- return nil, errors.Wrapf(define.ErrInvalidArg, "unsupported Cgroup manager: %s - cannot validate cgroup parent", r.config.Engine.CgroupManager)
}
if pod.config.UsePodCgroup {
@@ -236,7 +240,7 @@ func (r *Runtime) removePod(ctx context.Context, p *Pod, removeCtrs, force bool,
// Don't try if we failed to retrieve the cgroup
if err == nil {
- if err := conmonCgroup.Update(resLimits); err != nil {
+ if err := conmonCgroup.Update(resLimits); err != nil && !os.IsNotExist(err) {
logrus.Warnf("Error updating pod %s conmon cgroup PID limit: %v", p.ID(), err)
}
}
diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go
index c4fe3db90..3d585fa7a 100644
--- a/libpod/runtime_volume_linux.go
+++ b/libpod/runtime_volume_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/stats.go b/libpod/stats.go
index dbb10a27e..25baa378d 100644
--- a/libpod/stats.go
+++ b/libpod/stats.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
@@ -13,7 +14,9 @@ import (
"github.com/pkg/errors"
)
-// GetContainerStats gets the running stats for a given container
+// GetContainerStats gets the running stats for a given container.
+// The previousStats is used to correctly calculate cpu percentages. You
+// should pass nil if there is no previous stat for this container.
func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*define.ContainerStats, error) {
stats := new(define.ContainerStats)
stats.ContainerID = c.ID()
@@ -35,6 +38,14 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
return stats, define.ErrCtrStateInvalid
}
+ if previousStats == nil {
+ previousStats = &define.ContainerStats{
+ // if we have no prev stats use the container start time as prev time
+ // otherwise we cannot correctly calculate the CPU percentage
+ SystemNano: uint64(c.state.StartedTime.UnixNano()),
+ }
+ }
+
cgroupPath, err := c.cGroupPath()
if err != nil {
return nil, err
@@ -66,8 +77,8 @@ func (c *Container) GetContainerStats(previousStats *define.ContainerStats) (*de
stats.Duration = cgroupStats.CPU.Usage.Total
stats.UpTime = time.Duration(stats.Duration)
stats.CPU = calculateCPUPercent(cgroupStats, previousCPU, now, previousStats.SystemNano)
- stats.AvgCPU = calculateAvgCPU(stats.CPU, previousStats.AvgCPU, previousStats.DataPoints)
- stats.DataPoints = previousStats.DataPoints + 1
+ // calc the average cpu usage for the time the container is running
+ stats.AvgCPU = calculateCPUPercent(cgroupStats, 0, now, uint64(c.state.StartedTime.UnixNano()))
stats.MemUsage = cgroupStats.Memory.Usage.Usage
stats.MemLimit = c.getMemLimit()
stats.MemPerc = (float64(stats.MemUsage) / float64(stats.MemLimit)) * 100
@@ -145,9 +156,3 @@ func calculateBlockIO(stats *cgroups.Metrics) (read uint64, write uint64) {
}
return
}
-
-// calculateAvgCPU calculates the avg CPU percentage given the previous average and the number of data points.
-func calculateAvgCPU(statsCPU float64, prevAvg float64, prevData int64) float64 {
- avgPer := ((prevAvg * float64(prevData)) + statsCPU) / (float64(prevData) + 1)
- return avgPer
-}
diff --git a/libpod/util_linux.go b/libpod/util_linux.go
index dd115c7fb..fe98056dc 100644
--- a/libpod/util_linux.go
+++ b/libpod/util_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod
diff --git a/libpod/volume.go b/libpod/volume.go
index d60d978ed..f79ceaa87 100644
--- a/libpod/volume.go
+++ b/libpod/volume.go
@@ -255,3 +255,16 @@ func (v *Volume) IsDangling() (bool, error) {
func (v *Volume) UsesVolumeDriver() bool {
return !(v.config.Driver == define.VolumeDriverLocal || v.config.Driver == "")
}
+
+func (v *Volume) Mount() (string, error) {
+ v.lock.Lock()
+ defer v.lock.Unlock()
+ err := v.mount()
+ return v.config.MountPoint, err
+}
+
+func (v *Volume) Unmount() error {
+ v.lock.Lock()
+ defer v.lock.Unlock()
+ return v.unmount(false)
+}
diff --git a/libpod/volume_internal_linux.go b/libpod/volume_internal_linux.go
index 60d3667a9..7d7dea9d0 100644
--- a/libpod/volume_internal_linux.go
+++ b/libpod/volume_internal_linux.go
@@ -1,3 +1,4 @@
+//go:build linux
// +build linux
package libpod