Skip to content

Commit

Permalink
Merge pull request #6747 from giuseppe/fix-user-volumes
Browse files Browse the repository at this point in the history
container: move volume chown after spec generation
  • Loading branch information
openshift-merge-robot authored Jun 30, 2020
2 parents 83bde3b + ce74c20 commit c2a0ccd
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 17 deletions.
52 changes: 50 additions & 2 deletions libpod/container_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,12 @@ func (c *Container) init(ctx context.Context, retainRetries bool) error {
return err
}

for _, v := range c.config.NamedVolumes {
if err := c.chownVolume(v.Name); err != nil {
return err
}
}

// 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
Expand Down Expand Up @@ -1508,6 +1514,48 @@ func (c *Container) mountNamedVolume(v *ContainerNamedVolume, mountpoint string)
return vol, nil
}

// Chown the specified volume if necessary.
func (c *Container) chownVolume(volumeName string) error {
vol, err := c.runtime.state.Volume(volumeName)
if err != nil {
return errors.Wrapf(err, "error retrieving named volume %s for container %s", volumeName, c.ID())
}

uid := int(c.config.Spec.Process.User.UID)
gid := int(c.config.Spec.Process.User.GID)

vol.lock.Lock()
defer vol.lock.Unlock()

// The volume may need a copy-up. Check the state.
if err := vol.update(); err != nil {
return err
}

if vol.state.NeedsChown {
vol.state.NeedsChown = false
vol.state.UIDChowned = uid
vol.state.GIDChowned = gid

if err := vol.save(); err != nil {
return err
}
err := filepath.Walk(vol.MountPoint(), func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if err := os.Chown(path, uid, gid); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
}
return nil
}

// cleanupStorage unmounts and cleans up the container's root filesystem
func (c *Container) cleanupStorage() error {
if !c.state.Mounted {
Expand Down Expand Up @@ -1854,8 +1902,8 @@ func (c *Container) unmount(force bool) error {
// this should be from chrootarchive.
// Container MUST be mounted before calling.
func (c *Container) copyWithTarFromImage(source, dest string) error {
a := archive.NewDefaultArchiver()

mappings := idtools.NewIDMappingsFromMaps(c.config.IDMappings.UIDMap, c.config.IDMappings.GIDMap)
a := archive.NewArchiver(mappings)
if err := c.copyOwnerAndPerms(source, dest); err != nil {
return err
}
Expand Down
13 changes: 13 additions & 0 deletions libpod/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -1497,6 +1497,19 @@ func WithVolumeGID(gid int) VolumeCreateOption {
}
}

// WithVolumeNeedsChown sets the NeedsChown flag for the volume.
func WithVolumeNeedsChown() VolumeCreateOption {
return func(volume *Volume) error {
if volume.valid {
return define.ErrVolumeFinalized
}

volume.state.NeedsChown = true

return nil
}
}

// withSetAnon sets a bool notifying libpod that this volume is anonymous and
// should be removed when containers using it are removed and volumes are
// specified for removal.
Expand Down
2 changes: 1 addition & 1 deletion libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
logrus.Debugf("Creating new volume %s for container", vol.Name)

// The volume does not exist, so we need to create it.
volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID())}
volOptions := []VolumeCreateOption{WithVolumeName(vol.Name), WithVolumeUID(ctr.RootUID()), WithVolumeGID(ctr.RootGID()), WithVolumeNeedsChown()}
if isAnonymous {
volOptions = append(volOptions, withSetAnon())
}
Expand Down
36 changes: 32 additions & 4 deletions libpod/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@ type VolumeState struct {
// create time, then cleared after the copy up is done and never set
// again.
NeedsCopyUp bool `json:"notYetMounted,omitempty"`
// NeedsChown indicates that the next time the volume is mounted into
// a container, the container will chown the volume to the container process
// UID/GID.
NeedsChown bool `json:"notYetChowned,omitempty"`
// UIDChowned is the UID the volume was chowned to.
UIDChowned int `json:"uidChowned,omitempty"`
// GIDChowned is the GID the volume was chowned to.
GIDChowned int `json:"gidChowned,omitempty"`
}

// Name retrieves the volume's name
Expand Down Expand Up @@ -113,13 +121,33 @@ func (v *Volume) Anonymous() bool {
}

// UID returns the UID the volume will be created as.
func (v *Volume) UID() int {
return v.config.UID
func (v *Volume) UID() (int, error) {
v.lock.Lock()
defer v.lock.Unlock()

if !v.valid {
return -1, define.ErrVolumeRemoved
}

if v.state.UIDChowned > 0 {
return v.state.UIDChowned, nil
}
return v.config.UID, nil
}

// GID returns the GID the volume will be created as.
func (v *Volume) GID() int {
return v.config.GID
func (v *Volume) GID() (int, error) {
v.lock.Lock()
defer v.lock.Unlock()

if !v.valid {
return -1, define.ErrVolumeRemoved
}

if v.state.GIDChowned > 0 {
return v.state.GIDChowned, nil
}
return v.config.GID, nil
}

// CreatedTime returns the time the volume was created at. It was not tracked
Expand Down
11 changes: 9 additions & 2 deletions libpod/volume_inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,15 @@ func (v *Volume) Inspect() (*InspectVolumeData, error) {
for k, v := range v.config.Options {
data.Options[k] = v
}
data.UID = v.config.UID
data.GID = v.config.GID
var err error
data.UID, err = v.UID()
if err != nil {
return nil, err
}
data.GID, err = v.GID()
if err != nil {
return nil, err
}
data.Anonymous = v.config.IsAnon

return data, nil
Expand Down
30 changes: 26 additions & 4 deletions pkg/api/handlers/libpod/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,17 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) {
utils.VolumeNotFound(w, name, err)
return
}
var uid, gid int
uid, err = vol.UID()
if err != nil {
utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err)
return
}
gid, err = vol.GID()
if err != nil {
utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err)
return
}
volResponse := entities.VolumeConfigResponse{
Name: vol.Name(),
Driver: vol.Driver(),
Expand All @@ -94,8 +105,8 @@ func InspectVolume(w http.ResponseWriter, r *http.Request) {
Labels: vol.Labels(),
Scope: vol.Scope(),
Options: vol.Options(),
UID: vol.UID(),
GID: vol.GID(),
UID: uid,
GID: gid,
}
utils.WriteResponse(w, http.StatusOK, volResponse)
}
Expand Down Expand Up @@ -130,6 +141,17 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
}
volumeConfigs := make([]*entities.VolumeListReport, 0, len(vols))
for _, v := range vols {
var uid, gid int
uid, err = v.UID()
if err != nil {
utils.Error(w, "Error fetching volume UID", http.StatusInternalServerError, err)
return
}
gid, err = v.GID()
if err != nil {
utils.Error(w, "Error fetching volume GID", http.StatusInternalServerError, err)
return
}
config := entities.VolumeConfigResponse{
Name: v.Name(),
Driver: v.Driver(),
Expand All @@ -138,8 +160,8 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
Labels: v.Labels(),
Scope: v.Scope(),
Options: v.Options(),
UID: v.UID(),
GID: v.GID(),
UID: uid,
GID: gid,
}
volumeConfigs = append(volumeConfigs, &entities.VolumeListReport{VolumeConfigResponse: config})
}
Expand Down
26 changes: 22 additions & 4 deletions pkg/domain/infra/abi/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
}
reports := make([]*entities.VolumeInspectReport, 0, len(vols))
for _, v := range vols {
var uid, gid int
uid, err = v.UID()
if err != nil {
return nil, err
}
gid, err = v.GID()
if err != nil {
return nil, err
}
config := entities.VolumeConfigResponse{
Name: v.Name(),
Driver: v.Driver(),
Expand All @@ -103,8 +112,8 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
Labels: v.Labels(),
Scope: v.Scope(),
Options: v.Options(),
UID: v.UID(),
GID: v.GID(),
UID: uid,
GID: gid,
}
reports = append(reports, &entities.VolumeInspectReport{VolumeConfigResponse: &config})
}
Expand Down Expand Up @@ -141,6 +150,15 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL
}
reports := make([]*entities.VolumeListReport, 0, len(vols))
for _, v := range vols {
var uid, gid int
uid, err = v.UID()
if err != nil {
return nil, err
}
gid, err = v.GID()
if err != nil {
return nil, err
}
config := entities.VolumeConfigResponse{
Name: v.Name(),
Driver: v.Driver(),
Expand All @@ -149,8 +167,8 @@ func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeL
Labels: v.Labels(),
Scope: v.Scope(),
Options: v.Options(),
UID: v.UID(),
GID: v.GID(),
UID: uid,
GID: gid,
}
reports = append(reports, &entities.VolumeListReport{VolumeConfigResponse: config})
}
Expand Down
27 changes: 27 additions & 0 deletions test/e2e/run_userns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,31 @@ var _ = Describe("Podman UserNS support", func() {
ok, _ := session.GrepString("4998")
Expect(ok).To(BeTrue())
})

It("podman --user with volume", func() {
tests := []struct {
uid, gid, arg, vol string
}{
{"0", "0", "0:0", "vol-0"},
{"1000", "0", "1000", "vol-1"},
{"1000", "1000", "1000:1000", "vol-2"},
}

for _, tt := range tests {
session := podmanTest.Podman([]string{"run", "-d", "--user", tt.arg, "--mount", "type=volume,src=" + tt.vol + ",dst=/home/user", "alpine", "top"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Equal(0))

inspectUID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .UID }}", tt.vol})
inspectUID.WaitWithDefaultTimeout()
Expect(inspectUID.ExitCode()).To(Equal(0))
Expect(inspectUID.OutputToString()).To(Equal(tt.uid))

// Make sure we're defaulting to 0.
inspectGID := podmanTest.Podman([]string{"volume", "inspect", "--format", "{{ .GID }}", tt.vol})
inspectGID.WaitWithDefaultTimeout()
Expect(inspectGID.ExitCode()).To(Equal(0))
Expect(inspectGID.OutputToString()).To(Equal(tt.gid))
}
})
})

0 comments on commit c2a0ccd

Please sign in to comment.