diff options
Diffstat (limited to 'libpod')
-rw-r--r-- | libpod/boltdb_state_internal.go | 11 | ||||
-rw-r--r-- | libpod/common/common.go | 15 | ||||
-rw-r--r-- | libpod/container.go | 25 | ||||
-rw-r--r-- | libpod/container_internal.go | 47 | ||||
-rw-r--r-- | libpod/container_internal_linux.go | 25 | ||||
-rw-r--r-- | libpod/events.go | 6 | ||||
-rw-r--r-- | libpod/image/image.go | 5 | ||||
-rw-r--r-- | libpod/image/pull.go | 2 | ||||
-rw-r--r-- | libpod/image/search.go | 3 | ||||
-rw-r--r-- | libpod/info.go | 9 | ||||
-rw-r--r-- | libpod/oci.go | 10 | ||||
-rw-r--r-- | libpod/oci_linux.go | 91 | ||||
-rw-r--r-- | libpod/options.go | 35 | ||||
-rw-r--r-- | libpod/pod_api.go | 5 | ||||
-rw-r--r-- | libpod/runtime.go | 56 | ||||
-rw-r--r-- | libpod/runtime_ctr.go | 16 | ||||
-rw-r--r-- | libpod/runtime_volume_linux.go | 24 | ||||
-rw-r--r-- | libpod/util.go | 10 | ||||
-rw-r--r-- | libpod/util_linux.go | 21 | ||||
-rw-r--r-- | libpod/util_unsupported.go | 6 | ||||
-rw-r--r-- | libpod/volume.go | 2 |
21 files changed, 214 insertions, 210 deletions
diff --git a/libpod/boltdb_state_internal.go b/libpod/boltdb_state_internal.go index 936ccbf4c..b6a0759b1 100644 --- a/libpod/boltdb_state_internal.go +++ b/libpod/boltdb_state_internal.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/boltdb/bolt" + "github.com/containers/libpod/pkg/rootless" "github.com/containers/storage" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -95,22 +96,26 @@ func checkRuntimeConfig(db *bolt.DB, rt *Runtime) error { return err } + storeOpts, err := storage.DefaultStoreOptions(rootless.IsRootless(), rootless.GetRootlessUID()) + if err != nil { + return err + } if err := validateDBAgainstConfig(configBkt, "storage temporary directory (runroot)", rt.config.StorageConfig.RunRoot, runRootKey, - storage.DefaultStoreOptions.RunRoot); err != nil { + storeOpts.RunRoot); err != nil { return err } if err := validateDBAgainstConfig(configBkt, "storage graph root directory (graphroot)", rt.config.StorageConfig.GraphRoot, graphRootKey, - storage.DefaultStoreOptions.GraphRoot); err != nil { + storeOpts.GraphRoot); err != nil { return err } if err := validateDBAgainstConfig(configBkt, "storage graph driver", rt.config.StorageConfig.GraphDriverName, graphDriverKey, - storage.DefaultStoreOptions.GraphDriverName); err != nil { + storeOpts.GraphDriverName); err != nil { return err } diff --git a/libpod/common/common.go b/libpod/common/common.go index 5d10bee36..93a736af2 100644 --- a/libpod/common/common.go +++ b/libpod/common/common.go @@ -1,20 +1,5 @@ package common -import ( - "github.com/containers/image/types" -) - -// GetSystemContext Constructs a new containers/image/types.SystemContext{} struct from the given signaturePolicy path -func GetSystemContext(signaturePolicyPath, authFilePath string, forceCompress bool) *types.SystemContext { - sc := &types.SystemContext{} - if signaturePolicyPath != "" { - sc.SignaturePolicyPath = signaturePolicyPath - } - sc.AuthFilePath = authFilePath - sc.DirForceCompress = forceCompress - return sc -} - // IsTrue determines whether the given string equals "true" func IsTrue(str string) bool { return str == "true" diff --git a/libpod/container.go b/libpod/container.go index 806e75c63..739406e42 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -17,7 +17,6 @@ import ( "github.com/cri-o/ocicni/pkg/ocicni" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" - "github.com/ulule/deepcopier" ) // ContainerStatus represents the current state of a container @@ -147,18 +146,12 @@ type ContainerState struct { ConfigPath string `json:"configPath,omitempty"` // RunDir is a per-boot directory for container content RunDir string `json:"runDir,omitempty"` - // DestinationRunDir is where the files in RunDir will be accessible for the container. - // It is different than RunDir when using userNS - DestinationRunDir string `json:"destinationRunDir,omitempty"` // Mounted indicates whether the container's storage has been mounted // for use Mounted bool `json:"mounted,omitempty"` // Mountpoint contains the path to the container's mounted storage as given - // by containers/storage. It can be different than RealMountpoint when - // usernamespaces are used + // by containers/storage. Mountpoint string `json:"mountPoint,omitempty"` - // RealMountpoint contains the path to the container's mounted storage - RealMountpoint string `json:"realMountPoint,omitempty"` // StartedTime is the time the container was started StartedTime time.Time `json:"startedTime,omitempty"` // FinishedTime is the time the container finished executing @@ -187,10 +180,6 @@ type ContainerState struct { // the path of the file on disk outside the container BindMounts map[string]string `json:"bindMounts,omitempty"` - // UserNSRoot is the directory used as root for the container when using - // user namespaces. - UserNSRoot string `json:"userNSRoot,omitempty"` - // ExtensionStageHooks holds hooks which will be executed by libpod // and not delegated to the OCI runtime. ExtensionStageHooks map[string][]spec.Hook `json:"extensionStageHooks,omitempty"` @@ -407,7 +396,9 @@ func (t ContainerStatus) String() string { // Config returns the configuration used to create the container func (c *Container) Config() *ContainerConfig { returnConfig := new(ContainerConfig) - deepcopier.Copy(c.config).To(returnConfig) + if err := JSONDeepCopy(c.config, returnConfig); err != nil { + return nil + } return returnConfig } @@ -417,7 +408,9 @@ func (c *Container) Config() *ContainerConfig { // spec may differ slightly as mounts are added based on the image func (c *Container) Spec() *spec.Spec { returnSpec := new(spec.Spec) - deepcopier.Copy(c.config.Spec).To(returnSpec) + if err := JSONDeepCopy(c.config.Spec, returnSpec); err != nil { + return nil + } return returnSpec } @@ -1094,7 +1087,9 @@ func (c *Container) ContainerState() (*ContainerState, error) { } } returnConfig := new(ContainerState) - deepcopier.Copy(c.state).To(returnConfig) + if err := JSONDeepCopy(c.state, returnConfig); err != nil { + return nil, errors.Wrapf(err, "error copying container %s state", c.ID()) + } return c.state, nil } diff --git a/libpod/container_internal.go b/libpod/container_internal.go index 7a90bc7d4..daa32007a 100644 --- a/libpod/container_internal.go +++ b/libpod/container_internal.go @@ -310,23 +310,12 @@ func (c *Container) setupStorage(ctx context.Context) error { } if !rootless.IsRootless() && (len(c.config.IDMappings.UIDMap) != 0 || len(c.config.IDMappings.GIDMap) != 0) { - info, err := os.Stat(c.runtime.config.TmpDir) - if err != nil { - return errors.Wrapf(err, "cannot stat `%s`", c.runtime.config.TmpDir) - } - if err := os.Chmod(c.runtime.config.TmpDir, info.Mode()|0111); err != nil { - return errors.Wrapf(err, "cannot chmod `%s`", c.runtime.config.TmpDir) - } - root := filepath.Join(c.runtime.config.TmpDir, "containers-root", c.ID()) - if err := os.MkdirAll(root, 0755); err != nil { - return errors.Wrapf(err, "error creating userNS tmpdir for container %s", c.ID()) - } - if err := os.Chown(root, c.RootUID(), c.RootGID()); err != nil { + if err := os.Chown(containerInfo.RunDir, c.RootUID(), c.RootGID()); err != nil { return err } - c.state.UserNSRoot, err = filepath.EvalSymlinks(root) - if err != nil { - return errors.Wrapf(err, "failed to eval symlinks for %s", root) + + if err := os.Chown(containerInfo.Dir, c.RootUID(), c.RootGID()); err != nil { + return err } } @@ -334,10 +323,6 @@ func (c *Container) setupStorage(ctx context.Context) error { c.config.MountLabel = containerInfo.MountLabel c.config.StaticDir = containerInfo.Dir c.state.RunDir = containerInfo.RunDir - c.state.DestinationRunDir = c.state.RunDir - if c.state.UserNSRoot != "" { - c.state.DestinationRunDir = filepath.Join(c.state.UserNSRoot, "rundir") - } // Set the default Entrypoint and Command if containerInfo.Config != nil { @@ -372,12 +357,6 @@ func (c *Container) teardownStorage() error { return errors.Wrapf(err, "failed to cleanup container %s storage", c.ID()) } - if c.state.UserNSRoot != "" { - if err := os.RemoveAll(c.state.UserNSRoot); err != nil { - return errors.Wrapf(err, "error removing userns root %q", c.state.UserNSRoot) - } - } - if err := c.runtime.storageService.DeleteContainer(c.ID()); err != nil { // If the container has already been removed, warn but do not // error - we wanted it gone, it is already gone. @@ -432,6 +411,7 @@ func (c *Container) refresh() error { if err != nil { return errors.Wrapf(err, "error retrieving temporary directory for container %s", c.ID()) } + c.state.RunDir = dir if len(c.config.IDMappings.UIDMap) != 0 || len(c.config.IDMappings.GIDMap) != 0 { info, err := os.Stat(c.runtime.config.TmpDir) @@ -448,16 +428,6 @@ func (c *Container) refresh() error { if err := os.Chown(root, c.RootUID(), c.RootGID()); err != nil { return err } - c.state.UserNSRoot, err = filepath.EvalSymlinks(root) - if err != nil { - return errors.Wrapf(err, "failed to eval symlinks for %s", root) - } - } - - c.state.RunDir = dir - c.state.DestinationRunDir = c.state.RunDir - if c.state.UserNSRoot != "" { - c.state.DestinationRunDir = filepath.Join(c.state.UserNSRoot, "rundir") } // We need to pick up a new lock @@ -1260,7 +1230,7 @@ func (c *Container) writeStringToRundir(destFile, output string) (string, error) return "", err } - return filepath.Join(c.state.DestinationRunDir, destFile), nil + return filepath.Join(c.state.RunDir, destFile), nil } // appendStringToRundir appends the provided string to the runtimedir file @@ -1277,7 +1247,7 @@ func (c *Container) appendStringToRundir(destFile, output string) (string, error return "", errors.Wrapf(err, "unable to write %s", destFileName) } - return filepath.Join(c.state.DestinationRunDir, destFile), nil + return filepath.Join(c.state.RunDir, destFile), nil } // Save OCI spec to disk, replacing any existing specs for the container @@ -1410,6 +1380,9 @@ func (c *Container) mount() (string, error) { if err != nil { return "", errors.Wrapf(err, "error resolving storage path for container %s", c.ID()) } + if err := os.Chown(mountPoint, c.RootUID(), c.RootGID()); err != nil { + return "", errors.Wrapf(err, "cannot chown %s to %d:%d", mountPoint, c.RootUID(), c.RootGID()) + } return mountPoint, nil } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 02f8d6aa4..504d6c135 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -25,7 +25,6 @@ import ( "github.com/containers/libpod/pkg/lookup" "github.com/containers/libpod/pkg/resolvconf" "github.com/containers/libpod/pkg/rootless" - "github.com/containers/storage/pkg/idtools" "github.com/cyphar/filepath-securejoin" "github.com/opencontainers/runc/libcontainer/user" spec "github.com/opencontainers/runtime-spec/specs-go" @@ -99,11 +98,6 @@ func (c *Container) prepare() (err error) { // Finish up mountStorage c.state.Mounted = true c.state.Mountpoint = mountPoint - if c.state.UserNSRoot == "" { - c.state.RealMountpoint = c.state.Mountpoint - } else { - c.state.RealMountpoint = filepath.Join(c.state.UserNSRoot, "mountpoint") - } logrus.Debugf("Created root filesystem for container %s at %s", c.ID(), c.state.Mountpoint) }() @@ -220,13 +214,6 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } } m.Options = options - - // If we are using a user namespace, we will use an intermediate - // directory to bind mount volumes - if c.state.UserNSRoot != "" && strings.HasPrefix(m.Source, c.runtime.config.VolumePath) { - newSourceDir := filepath.Join(c.state.UserNSRoot, "volumes") - m.Source = strings.Replace(m.Source, c.runtime.config.VolumePath, newSourceDir, 1) - } } g.SetProcessSelinuxLabel(c.ProcessLabel()) @@ -313,13 +300,7 @@ func (c *Container) generateSpec(ctx context.Context) (*spec.Spec, error) { } } - if c.config.Rootfs == "" { - if err := idtools.MkdirAllAs(c.state.RealMountpoint, 0700, c.RootUID(), c.RootGID()); err != nil { - return nil, err - } - } - - g.SetRootPath(c.state.RealMountpoint) + g.SetRootPath(c.state.Mountpoint) g.AddAnnotation(crioAnnotations.Created, c.config.CreatedTime.Format(time.RFC3339Nano)) g.AddAnnotation("org.opencontainers.image.stopSignal", fmt.Sprintf("%d", c.config.StopSignal)) @@ -820,7 +801,7 @@ func (c *Container) makeBindMounts() error { } // Add Secret Mounts - secretMounts := secrets.SecretMountsWithUIDGID(c.config.MountLabel, c.state.RunDir, c.runtime.config.DefaultMountsFile, c.state.DestinationRunDir, c.RootUID(), c.RootGID()) + secretMounts := secrets.SecretMountsWithUIDGID(c.config.MountLabel, c.state.RunDir, c.runtime.config.DefaultMountsFile, c.state.RunDir, c.RootUID(), c.RootGID(), rootless.IsRootless()) for _, mount := range secretMounts { if _, ok := c.state.BindMounts[mount.Destination]; !ok { c.state.BindMounts[mount.Destination] = mount.Source @@ -907,7 +888,7 @@ func (c *Container) generateResolvConf() (string, error) { return "", err } - return filepath.Join(c.state.DestinationRunDir, "resolv.conf"), nil + return filepath.Join(c.state.RunDir, "resolv.conf"), nil } // generateHosts creates a containers hosts file diff --git a/libpod/events.go b/libpod/events.go index 139600982..b6a277789 100644 --- a/libpod/events.go +++ b/libpod/events.go @@ -58,6 +58,10 @@ func (v *Volume) newVolumeEvent(status events.Status) { // Events is a wrapper function for everyone to begin tailing the events log // with options func (r *Runtime) Events(fromStart, stream bool, options []events.EventFilter, eventChannel chan *events.Event) error { + if !r.valid { + return ErrRuntimeStopped + } + t, err := r.getTail(fromStart, stream) if err != nil { return err @@ -71,7 +75,7 @@ func (r *Runtime) Events(fromStart, stream bool, options []events.EventFilter, e case events.Image, events.Volume, events.Pod, events.Container: // no-op default: - return errors.Errorf("event type %s is not valid in %s", event.Type.String(), r.GetConfig().EventsLogFilePath) + return errors.Errorf("event type %s is not valid in %s", event.Type.String(), r.config.EventsLogFilePath) } include := true for _, filter := range options { diff --git a/libpod/image/image.go b/libpod/image/image.go index f79bc3dc2..4862bf1d6 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "os" + "path/filepath" "strings" "syscall" "time" @@ -22,7 +23,6 @@ import ( "github.com/containers/image/transports" "github.com/containers/image/transports/alltransports" "github.com/containers/image/types" - "github.com/containers/libpod/libpod/common" "github.com/containers/libpod/libpod/driver" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/pkg/inspect" @@ -548,6 +548,7 @@ func (i *Image) PushImageToHeuristicDestination(ctx context.Context, destination // PushImageToReference pushes the given image to a location described by the given path func (i *Image) PushImageToReference(ctx context.Context, dest types.ImageReference, manifestMIMEType, authFile, signaturePolicyPath string, writer io.Writer, forceCompress bool, signingOptions SigningOptions, dockerRegistryOptions *DockerRegistryOptions, additionalDockerArchiveTags []reference.NamedTagged) error { sc := GetSystemContext(signaturePolicyPath, authFile, forceCompress) + sc.BlobInfoCacheDir = filepath.Join(i.imageruntime.store.GraphRoot(), "cache") policyContext, err := getPolicyContext(sc) if err != nil { @@ -909,7 +910,7 @@ func (ir *Runtime) Import(ctx context.Context, path, reference string, writer io return nil, errors.Wrapf(err, "error updating image config") } - sc := common.GetSystemContext("", "", false) + sc := GetSystemContext("", "", false) // if reference not given, get the image digest if reference == "" { diff --git a/libpod/image/pull.go b/libpod/image/pull.go index a3b716e65..5a0706b07 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "path/filepath" "strings" cp "github.com/containers/image/copy" @@ -204,6 +205,7 @@ func (ir *Runtime) pullImageFromHeuristicSource(ctx context.Context, inputName s var goal *pullGoal sc := GetSystemContext(signaturePolicyPath, authfile, false) + sc.BlobInfoCacheDir = filepath.Join(ir.store.GraphRoot(), "cache") srcRef, err := alltransports.ParseImageName(inputName) if err != nil { // could be trying to pull from registry with short name diff --git a/libpod/image/search.go b/libpod/image/search.go index 212eff00b..2c66ce284 100644 --- a/libpod/image/search.go +++ b/libpod/image/search.go @@ -9,7 +9,6 @@ import ( "github.com/containers/image/docker" "github.com/containers/image/types" - "github.com/containers/libpod/libpod/common" sysreg "github.com/containers/libpod/pkg/registries" "github.com/fatih/camelcase" "github.com/pkg/errors" @@ -157,7 +156,7 @@ func searchImageInRegistry(term string, registry string, options SearchOptions) limit = options.Limit } - sc := common.GetSystemContext("", options.Authfile, false) + sc := GetSystemContext("", options.Authfile, false) sc.DockerInsecureSkipTLSVerify = options.InsecureSkipTLSVerify // FIXME: Set this more globally. Probably no reason not to have it in // every types.SystemContext, and to compute the value just once in one diff --git a/libpod/info.go b/libpod/info.go index 62088b730..b42f64a1f 100644 --- a/libpod/info.go +++ b/libpod/info.go @@ -13,8 +13,8 @@ import ( "github.com/containers/buildah" "github.com/containers/libpod/pkg/rootless" - "github.com/containers/libpod/pkg/util" "github.com/containers/libpod/utils" + "github.com/containers/storage" "github.com/containers/storage/pkg/system" "github.com/pkg/errors" ) @@ -116,12 +116,17 @@ func (r *Runtime) hostInfo() (map[string]interface{}, error) { func (r *Runtime) storeInfo() (map[string]interface{}, error) { // lets say storage driver in use, number of images, number of containers info := map[string]interface{}{} - info["ConfigFile"] = util.StorageConfigFile() info["GraphRoot"] = r.store.GraphRoot() info["RunRoot"] = r.store.RunRoot() info["GraphDriverName"] = r.store.GraphDriverName() info["GraphOptions"] = r.store.GraphOptions() info["VolumePath"] = r.config.VolumePath + + configFile, err := storage.DefaultConfigFile(rootless.IsRootless()) + if err != nil { + return nil, err + } + info["ConfigFile"] = configFile statusPairs, err := r.store.Status() if err != nil { return nil, err diff --git a/libpod/oci.go b/libpod/oci.go index 69cff6d3c..62331b879 100644 --- a/libpod/oci.go +++ b/libpod/oci.go @@ -325,8 +325,8 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res cmd.Env = append(r.conmonEnv, fmt.Sprintf("_OCI_SYNCPIPE=%d", 3)) cmd.Env = append(cmd.Env, fmt.Sprintf("_OCI_STARTPIPE=%d", 4)) cmd.Env = append(cmd.Env, fmt.Sprintf("XDG_RUNTIME_DIR=%s", runtimeDir)) - cmd.Env = append(cmd.Env, fmt.Sprintf("_LIBPOD_USERNS_CONFIGURED=%s", os.Getenv("_LIBPOD_USERNS_CONFIGURED"))) - cmd.Env = append(cmd.Env, fmt.Sprintf("_LIBPOD_ROOTLESS_UID=%s", os.Getenv("_LIBPOD_ROOTLESS_UID"))) + cmd.Env = append(cmd.Env, fmt.Sprintf("_CONTAINERS_USERNS_CONFIGURED=%s", os.Getenv("_CONTAINERS_USERNS_CONFIGURED"))) + cmd.Env = append(cmd.Env, fmt.Sprintf("_CONTAINERS_ROOTLESS_UID=%s", os.Getenv("_CONTAINERS_ROOTLESS_UID"))) cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", os.Getenv("HOME"))) if r.reservePorts && !ctr.config.NetMode.IsSlirp4netns() { @@ -473,7 +473,7 @@ func (r *OCIRuntime) createOCIContainer(ctr *Container, cgroupParent string, res // If useRunc is false, we will not directly hit runc to see the container's // status, but will instead only check for the existence of the conmon exit file // and update state to stopped if it exists. -func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRunc bool) error { +func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRuntime bool) error { exitFile := ctr.exitFilePath() runtimeDir, err := util.GetRootlessRuntimeDir() @@ -481,8 +481,8 @@ func (r *OCIRuntime) updateContainerStatus(ctr *Container, useRunc bool) error { return err } - // If not using runc, we don't need to do most of this. - if !useRunc { + // If not using the OCI runtime, we don't need to do most of this. + if !useRuntime { // If the container's not running, nothing to do. if ctr.state.State != ContainerStateRunning && ctr.state.State != ContainerStatePaused { return nil diff --git a/libpod/oci_linux.go b/libpod/oci_linux.go index f85c5ee62..8c0abad80 100644 --- a/libpod/oci_linux.go +++ b/libpod/oci_linux.go @@ -3,17 +3,14 @@ package libpod import ( - "fmt" "os" "os/exec" "path/filepath" - "runtime" "strings" - "sync" + "syscall" "github.com/containerd/cgroups" "github.com/containers/libpod/utils" - "github.com/containers/storage/pkg/idtools" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "golang.org/x/sys/unix" @@ -62,72 +59,40 @@ func newPipe() (parent *os.File, child *os.File, err error) { return os.NewFile(uintptr(fds[1]), "parent"), os.NewFile(uintptr(fds[0]), "child"), nil } -// CreateContainer creates a container in the OCI runtime -// TODO terminal support for container -// Presently just ignoring conmon opts related to it -func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restoreOptions *ContainerCheckpointOptions) (err error) { - if ctr.state.UserNSRoot == "" { - // no need of an intermediate mount ns - return r.createOCIContainer(ctr, cgroupParent, restoreOptions) - } - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - runtime.LockOSThread() - - var fd *os.File - fd, err = os.Open(fmt.Sprintf("/proc/%d/task/%d/ns/mnt", os.Getpid(), unix.Gettid())) +// makeAccessible changes the path permission and each parent directory to have --x--x--x +func makeAccessible(path string, uid, gid int) error { + for ; path != "/"; path = filepath.Dir(path) { + st, err := os.Stat(path) if err != nil { - return - } - defer fd.Close() - - // create a new mountns on the current thread - if err = unix.Unshare(unix.CLONE_NEWNS); err != nil { - return - } - defer unix.Setns(int(fd.Fd()), unix.CLONE_NEWNS) - - // don't spread our mounts around - err = unix.Mount("/", "/", "none", unix.MS_REC|unix.MS_SLAVE, "") - if err != nil { - return - } - err = unix.Mount(ctr.state.Mountpoint, ctr.state.RealMountpoint, "none", unix.MS_BIND, "") - if err != nil { - return + if os.IsNotExist(err) { + return nil + } + return err } - if err := idtools.MkdirAllAs(ctr.state.DestinationRunDir, 0700, ctr.RootUID(), ctr.RootGID()); err != nil { - return + if int(st.Sys().(*syscall.Stat_t).Uid) == uid && int(st.Sys().(*syscall.Stat_t).Gid) == gid { + continue } - - err = unix.Mount(ctr.state.RunDir, ctr.state.DestinationRunDir, "none", unix.MS_BIND, "") - if err != nil { - return + if st.Mode()&0111 != 0111 { + if err := os.Chmod(path, os.FileMode(st.Mode()|0111)); err != nil { + return err + } } + } + return nil +} - if ctr.state.UserNSRoot != "" { - _, err := os.Stat(ctr.runtime.config.VolumePath) - if err != nil && !os.IsNotExist(err) { - return - } - if err == nil { - volumesTarget := filepath.Join(ctr.state.UserNSRoot, "volumes") - if err := idtools.MkdirAs(volumesTarget, 0700, ctr.RootUID(), ctr.RootGID()); err != nil { - return - } - if err = unix.Mount(ctr.runtime.config.VolumePath, volumesTarget, "none", unix.MS_BIND, ""); err != nil { - return - } +// CreateContainer creates a container in the OCI runtime +// TODO terminal support for container +// Presently just ignoring conmon opts related to it +func (r *OCIRuntime) createContainer(ctr *Container, cgroupParent string, restoreOptions *ContainerCheckpointOptions) (err error) { + if len(ctr.config.IDMappings.UIDMap) != 0 || len(ctr.config.IDMappings.GIDMap) != 0 { + for _, i := range []string{ctr.state.RunDir, ctr.runtime.config.TmpDir, ctr.config.StaticDir, ctr.state.Mountpoint, ctr.runtime.config.VolumePath} { + if err := makeAccessible(i, ctr.RootUID(), ctr.RootGID()); err != nil { + return err } } - - err = r.createOCIContainer(ctr, cgroupParent, restoreOptions) - }() - wg.Wait() - - return err + } + return r.createOCIContainer(ctr, cgroupParent, restoreOptions) } func rpmVersion(path string) string { diff --git a/libpod/options.go b/libpod/options.go index 3ca80e96c..24f126e66 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -9,6 +9,7 @@ import ( "github.com/containers/image/manifest" "github.com/containers/libpod/pkg/namespaces" + "github.com/containers/libpod/pkg/rootless" "github.com/containers/storage" "github.com/containers/storage/pkg/idtools" "github.com/cri-o/ocicni/pkg/ocicni" @@ -17,7 +18,7 @@ import ( ) var ( - nameRegex = regexp.MustCompile("[a-zA-Z0-9_-]+") + nameRegex = regexp.MustCompile("^[a-zA-Z0-9][a-zA-Z0-9_.-]*$") ) // Runtime Creation Options @@ -82,11 +83,15 @@ func WithStorageConfig(config storage.StoreOptions) RuntimeOption { // or graphdriveroptions are set, then GraphRoot and RunRoot // must be set if setField { + storeOpts, err := storage.DefaultStoreOptions(rootless.IsRootless(), rootless.GetRootlessUID()) + if err != nil { + return err + } if rt.config.StorageConfig.GraphRoot == "" { - rt.config.StorageConfig.GraphRoot = storage.DefaultStoreOptions.GraphRoot + rt.config.StorageConfig.GraphRoot = storeOpts.GraphRoot } if rt.config.StorageConfig.RunRoot == "" { - rt.config.StorageConfig.RunRoot = storage.DefaultStoreOptions.RunRoot + rt.config.StorageConfig.RunRoot = storeOpts.RunRoot } } @@ -925,7 +930,7 @@ func WithNetNS(portMappings []ocicni.PortMapping, postConfigureNetNS bool, netmo ctr.config.PostConfigureNetNS = postConfigureNetNS ctr.config.NetMode = namespaces.NetworkMode(netmode) - ctr.config.CreateNetNS = !ctr.config.NetMode.IsUserDefined() + ctr.config.CreateNetNS = true ctr.config.PortMappings = portMappings ctr.config.Networks = networks @@ -1269,6 +1274,28 @@ func WithVolumeName(name string) VolumeCreateOption { } } +// WithVolumeUID sets the uid of the owner. +func WithVolumeUID(uid int) VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return ErrVolumeFinalized + } + volume.config.UID = uid + return nil + } +} + +// WithVolumeGID sets the gid of the owner. +func WithVolumeGID(gid int) VolumeCreateOption { + return func(volume *Volume) error { + if volume.valid { + return ErrVolumeFinalized + } + volume.config.GID = gid + return nil + } +} + // WithVolumeLabels sets the labels of the volume. func WithVolumeLabels(labels map[string]string) VolumeCreateOption { return func(volume *Volume) error { diff --git a/libpod/pod_api.go b/libpod/pod_api.go index b9a11000e..9a6baf23e 100644 --- a/libpod/pod_api.go +++ b/libpod/pod_api.go @@ -6,7 +6,6 @@ import ( "github.com/containers/libpod/libpod/events" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/ulule/deepcopier" ) // Start starts all containers within a pod @@ -441,7 +440,9 @@ func (p *Pod) Inspect() (*PodInspect, error) { infraContainerID := p.state.InfraContainerID config := new(PodConfig) - deepcopier.Copy(p.config).To(config) + if err := JSONDeepCopy(p.config, config); err != nil { + return nil, err + } inspectData := PodInspect{ Config: config, State: &PodInspectState{ diff --git a/libpod/runtime.go b/libpod/runtime.go index b3b75d791..6e54de558 100644 --- a/libpod/runtime.go +++ b/libpod/runtime.go @@ -23,7 +23,6 @@ import ( "github.com/docker/docker/pkg/namesgenerator" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/ulule/deepcopier" ) // RuntimeStateStore is a constant indicating which state store implementation @@ -249,11 +248,16 @@ type runtimeConfiguredFrom struct { noPivotRoot bool } -var ( - defaultRuntimeConfig = RuntimeConfig{ +func defaultRuntimeConfig() (RuntimeConfig, error) { + storeOpts, err := storage.DefaultStoreOptions(rootless.IsRootless(), rootless.GetRootlessUID()) + if err != nil { + return RuntimeConfig{}, err + } + + return RuntimeConfig{ // Leave this empty so containers/storage will use its defaults StorageConfig: storage.StoreOptions{}, - VolumePath: filepath.Join(storage.DefaultStoreOptions.GraphRoot, "volumes"), + VolumePath: filepath.Join(storeOpts.GraphRoot, "volumes"), ImageDefaultTransport: DefaultTransport, StateType: BoltDBStateStore, OCIRuntime: "runc", @@ -282,7 +286,7 @@ var ( }, InitPath: DefaultInitPath, CgroupManager: SystemdCgroupsManager, - StaticDir: filepath.Join(storage.DefaultStoreOptions.GraphRoot, "libpod"), + StaticDir: filepath.Join(storeOpts.GraphRoot, "libpod"), TmpDir: "", MaxLogSize: -1, NoPivotRoot: false, @@ -293,8 +297,8 @@ var ( EnablePortReservation: true, EnableLabeling: true, NumLocks: 2048, - } -) + }, nil +} func getDefaultTmpDir() (string, error) { if !rootless.IsRootless() { @@ -305,7 +309,17 @@ func getDefaultTmpDir() (string, error) { if err != nil { return "", err } - return filepath.Join(rootlessRuntimeDir, "libpod", "tmp"), nil + libpodRuntimeDir := filepath.Join(rootlessRuntimeDir, "libpod") + + if err := os.Mkdir(libpodRuntimeDir, 0700|os.ModeSticky); err != nil { + if !os.IsExist(err) { + return "", errors.Wrapf(err, "cannot mkdir %s", libpodRuntimeDir) + } else if err := os.Chmod(libpodRuntimeDir, 0700|os.ModeSticky); err != nil { + // The directory already exist, just set the sticky bit + return "", errors.Wrapf(err, "could not set sticky bit on %s", libpodRuntimeDir) + } + } + return filepath.Join(libpodRuntimeDir, "tmp"), nil } // SetXdgRuntimeDir ensures the XDG_RUNTIME_DIR env variable is set @@ -355,10 +369,17 @@ func newRuntimeFromConfig(userConfigPath string, options ...RuntimeOption) (runt if err != nil { return nil, err } - deepcopier.Copy(defaultRuntimeConfig).To(runtime.config) + + defRunConf, err := defaultRuntimeConfig() + if err != nil { + return nil, err + } + if err := JSONDeepCopy(defRunConf, runtime.config); err != nil { + return nil, errors.Wrapf(err, "error copying runtime default config") + } runtime.config.TmpDir = tmpDir - storageConf, err := util.GetDefaultStoreOptions() + storageConf, err := storage.DefaultStoreOptions(rootless.IsRootless(), rootless.GetRootlessUID()) if err != nil { return nil, errors.Wrapf(err, "error retrieving storage config") } @@ -507,7 +528,10 @@ func newRuntimeFromConfig(userConfigPath string, options ...RuntimeOption) (runt } if rootlessConfigPath != "" { // storage.conf - storageConfFile := util.StorageConfigFile() + storageConfFile, err := storage.DefaultConfigFile(rootless.IsRootless()) + if err != nil { + return nil, err + } if _, err := os.Stat(storageConfFile); os.IsNotExist(err) { if err := util.WriteStorageConfigFile(&runtime.config.StorageConfig, storageConfFile); err != nil { return nil, errors.Wrapf(err, "cannot write config file %s", storageConfFile) @@ -923,20 +947,22 @@ func makeRuntime(runtime *Runtime) (err error) { } // GetConfig returns a copy of the configuration used by the runtime -func (r *Runtime) GetConfig() *RuntimeConfig { +func (r *Runtime) GetConfig() (*RuntimeConfig, error) { r.lock.RLock() defer r.lock.RUnlock() if !r.valid { - return nil + return nil, ErrRuntimeStopped } config := new(RuntimeConfig) // Copy so the caller won't be able to modify the actual config - deepcopier.Copy(r.config).To(config) + if err := JSONDeepCopy(r.config, config); err != nil { + return nil, errors.Wrapf(err, "error copying config") + } - return config + return config, nil } // Shutdown shuts down the runtime and associated containers and storage diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go index f23dc86dd..506aee477 100644 --- a/libpod/runtime_ctr.go +++ b/libpod/runtime_ctr.go @@ -19,7 +19,6 @@ import ( opentracing "github.com/opentracing/opentracing-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" - "github.com/ulule/deepcopier" ) // CtrRemoveTimeout is the default number of seconds to wait after stopping a container @@ -63,7 +62,9 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options .. ctr.config.ID = stringid.GenerateNonCryptoID() ctr.config.Spec = new(spec.Spec) - deepcopier.Copy(rSpec).To(ctr.config.Spec) + if err := JSONDeepCopy(rSpec, ctr.config.Spec); err != nil { + return nil, errors.Wrapf(err, "error copying runtime spec while creating container") + } ctr.config.CreatedTime = time.Now() ctr.config.ShmSize = DefaultShmSize @@ -181,14 +182,11 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options .. if vol.Source[0] != '/' && isNamedVolume(vol.Source) { volInfo, err := r.state.Volume(vol.Source) if err != nil { - newVol, err := r.newVolume(ctx, WithVolumeName(vol.Source), withSetCtrSpecific()) + newVol, err := r.newVolume(ctx, WithVolumeName(vol.Source), withSetCtrSpecific(), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID())) if err != nil { return nil, errors.Wrapf(err, "error creating named volume %q", vol.Source) } ctr.config.Spec.Mounts[i].Source = newVol.MountPoint() - if err := os.Chown(ctr.config.Spec.Mounts[i].Source, ctr.RootUID(), ctr.RootGID()); err != nil { - return nil, errors.Wrapf(err, "cannot chown %q to %d:%d", ctr.config.Spec.Mounts[i].Source, ctr.RootUID(), ctr.RootGID()) - } if err := ctr.copyWithTarFromImage(ctr.config.Spec.Mounts[i].Destination, ctr.config.Spec.Mounts[i].Source); err != nil && !os.IsNotExist(err) { return nil, errors.Wrapf(err, "failed to copy content into new volume mount %q", vol.Source) } @@ -203,11 +201,7 @@ func (r *Runtime) newContainer(ctx context.Context, rSpec *spec.Spec, options .. } if !MountExists(ctr.config.Spec.Mounts, "/dev/shm") && ctr.config.ShmDir == "" { - if ctr.state.UserNSRoot == "" { - ctr.config.ShmDir = filepath.Join(ctr.bundlePath(), "shm") - } else { - ctr.config.ShmDir = filepath.Join(ctr.state.UserNSRoot, "shm") - } + ctr.config.ShmDir = filepath.Join(ctr.bundlePath(), "shm") if err := os.MkdirAll(ctr.config.ShmDir, 0700); err != nil { if !os.IsExist(err) { return nil, errors.Wrapf(err, "unable to create shm %q dir", ctr.config.ShmDir) diff --git a/libpod/runtime_volume_linux.go b/libpod/runtime_volume_linux.go index b51bb8213..db5c29242 100644 --- a/libpod/runtime_volume_linux.go +++ b/libpod/runtime_volume_linux.go @@ -10,7 +10,6 @@ import ( "github.com/containers/libpod/libpod/events" "github.com/containers/storage/pkg/stringid" - "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -52,19 +51,22 @@ func (r *Runtime) newVolume(ctx context.Context, options ...VolumeCreateOption) } // Create the mountpoint of this volume - fullVolPath := filepath.Join(r.config.VolumePath, volume.config.Name, "_data") - if err := os.MkdirAll(fullVolPath, 0755); err != nil { - return nil, errors.Wrapf(err, "error creating volume directory %q", fullVolPath) + volPathRoot := filepath.Join(r.config.VolumePath, volume.config.Name) + if err := os.MkdirAll(volPathRoot, 0700); err != nil { + return nil, errors.Wrapf(err, "error creating volume directory %q", volPathRoot) } - _, mountLabel, err := label.InitLabels([]string{}) - if err != nil { - return nil, errors.Wrapf(err, "error getting default mountlabels") + if err := os.Chown(volPathRoot, volume.config.UID, volume.config.GID); err != nil { + return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", volPathRoot, volume.config.UID, volume.config.GID) + } + fullVolPath := filepath.Join(volPathRoot, "_data") + if err := os.Mkdir(fullVolPath, 0755); err != nil { + return nil, errors.Wrapf(err, "error creating volume directory %q", fullVolPath) } - if err := label.ReleaseLabel(mountLabel); err != nil { - return nil, errors.Wrapf(err, "error releasing label %q", mountLabel) + if err := os.Chown(fullVolPath, volume.config.UID, volume.config.GID); err != nil { + return nil, errors.Wrapf(err, "error chowning volume directory %q to %d:%d", fullVolPath, volume.config.UID, volume.config.GID) } - if err := label.Relabel(fullVolPath, mountLabel, true); err != nil { - return nil, errors.Wrapf(err, "error setting selinux label to %q", fullVolPath) + if err := LabelVolumePath(fullVolPath, true); err != nil { + return nil, err } volume.config.MountPoint = fullVolPath diff --git a/libpod/util.go b/libpod/util.go index b7578135a..7e2dff21a 100644 --- a/libpod/util.go +++ b/libpod/util.go @@ -187,3 +187,13 @@ func validPodNSOption(p *Pod, ctrPod string) error { } return nil } + +// JSONDeepCopy performs a deep copy by performing a JSON encode/decode of the +// given structures. From and To should be identically typed structs. +func JSONDeepCopy(from, to interface{}) error { + tmp, err := json.Marshal(from) + if err != nil { + return err + } + return json.Unmarshal(tmp, to) +} diff --git a/libpod/util_linux.go b/libpod/util_linux.go index 30e2538c3..a801df2ee 100644 --- a/libpod/util_linux.go +++ b/libpod/util_linux.go @@ -9,6 +9,7 @@ import ( "github.com/containerd/cgroups" "github.com/containers/libpod/pkg/util" spec "github.com/opencontainers/runtime-spec/specs-go" + "github.com/opencontainers/selinux/go-selinux/label" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -91,3 +92,23 @@ func GetV1CGroups(excludes []string) cgroups.Hierarchy { return filtered, nil } } + +// LabelVolumePath takes a mount path for a volume and gives it an +// selinux label of either shared or not +func LabelVolumePath(path string, shared bool) error { + _, mountLabel, err := label.InitLabels([]string{}) + if err != nil { + return errors.Wrapf(err, "error getting default mountlabels") + } + if err := label.ReleaseLabel(mountLabel); err != nil { + return errors.Wrapf(err, "error releasing label %q", mountLabel) + } + if err := label.Relabel(path, mountLabel, shared); err != nil { + permString := "private" + if shared { + permString = "shared" + } + return errors.Wrapf(err, "error setting selinux label for %s to %q as %s", path, mountLabel, permString) + } + return nil +} diff --git a/libpod/util_unsupported.go b/libpod/util_unsupported.go index d598b465f..940006e69 100644 --- a/libpod/util_unsupported.go +++ b/libpod/util_unsupported.go @@ -21,3 +21,9 @@ func deleteSystemdCgroup(path string) error { func assembleSystemdCgroupName(baseSlice, newSlice string) (string, error) { return "", errors.Wrapf(ErrOSNotSupported, "cgroups are not supported on non-linux OSes") } + +// LabelVolumePath takes a mount path for a volume and gives it an +// selinux label of either shared or not +func LabelVolumePath(path string, shared bool) error { + return ErrNotImplemented +} diff --git a/libpod/volume.go b/libpod/volume.go index 0c7618841..0b37d44ef 100644 --- a/libpod/volume.go +++ b/libpod/volume.go @@ -21,6 +21,8 @@ type VolumeConfig struct { Options map[string]string `json:"options"` Scope string `json:"scope"` IsCtrSpecific bool `json:"ctrSpecific"` + UID int `json:"uid"` + GID int `json:"gid"` } // Name retrieves the volume's name |