Skip to content

Commit

Permalink
Ensure WORKDIR from images is created
Browse files Browse the repository at this point in the history
A recent crun change stopped the creation of the container's
working directory if it does not exist. This is arguably correct
for user-specified directories, to protect against typos; it is
definitely not correct for image WORKDIR, where the image author
definitely intended for the directory to be used.

This makes Podman create the working directory and chown it to
container root, if it does not already exist, and only if it was
specified by an image, not the user.

Signed-off-by: Matthew Heon <[email protected]>
  • Loading branch information
mheon committed Aug 20, 2020
1 parent 66fcafa commit d4c3365
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 1 deletion.
4 changes: 4 additions & 0 deletions libpod/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,10 @@ type ContainerConfig struct {
Mounts []string `json:"mounts,omitempty"`
// NamedVolumes lists the named volumes to mount into the container.
NamedVolumes []*ContainerNamedVolume `json:"namedVolumes,omitempty"`
// CreateWorkingDir indicates that Libpod should create the container's
// working directory if it does not exist. Some OCI runtimes do this by
// default, but others do not.
CreateWorkingDir bool `json:"createWorkingDir,omitempty"`

// Security Config

Expand Down
27 changes: 26 additions & 1 deletion libpod/container_internal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,32 @@ func (c *Container) prepare() error {
}

// Save changes to container state
return c.save()
if err := c.save(); err != nil {
return err
}

// Ensure container entrypoint is created (if required)
if c.config.CreateWorkingDir {
workdir, err := securejoin.SecureJoin(c.state.Mountpoint, c.WorkingDir())
if err != nil {
return errors.Wrapf(err, "error creating path to container %s working dir", c.ID())
}
rootUID := c.RootUID()
rootGID := c.RootGID()

if err := os.MkdirAll(workdir, 0755); err != nil {
if os.IsExist(err) {
return nil
}
return errors.Wrapf(err, "error creating container %s working dir", c.ID())
}

if err := os.Chown(workdir, rootUID, rootGID); err != nil {
return errors.Wrapf(err, "error chowning container %s working directory to container root", c.ID())
}
}

return nil
}

// cleanupNetwork unmounts and cleans up the container's network
Expand Down
13 changes: 13 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -1395,6 +1395,19 @@ func WithCreateCommand(cmd []string) CtrCreateOption {
}
}

// WithCreateWorkingDir tells Podman to create the container's working directory
// if it does not exist.
func WithCreateWorkingDir() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}

ctr.config.CreateWorkingDir = true
return nil
}
}

// Volume Creation Options

// WithVolumeName sets the name of the volume.
Expand Down
11 changes: 11 additions & 0 deletions pkg/specgen/generate/container_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,17 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
if s.Entrypoint != nil {
options = append(options, libpod.WithEntrypoint(s.Entrypoint))
}
// If the user did not set an workdir but the image did, ensure it is
// created.
if s.WorkDir == "" && img != nil {
newWD, err := img.WorkingDir(ctx)
if err != nil {
return nil, err
}
if newWD != "" {
options = append(options, libpod.WithCreateWorkingDir())
}
}
if s.StopSignal != nil {
options = append(options, libpod.WithStopSignal(*s.StopSignal))
}
Expand Down
10 changes: 10 additions & 0 deletions test/e2e/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1060,4 +1060,14 @@ USER mail`
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring(limit))
})

It("podman run makes entrypoint from image", func() {
dockerfile := `FROM busybox
WORKDIR /madethis`
podmanTest.BuildImage(dockerfile, "test", "false")
session := podmanTest.Podman([]string{"run", "--rm", "test", "pwd"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(ContainSubstring("/madethis"))
})
})

0 comments on commit d4c3365

Please sign in to comment.