Skip to content

Commit

Permalink
Merge pull request #1953 from akihikodaki/overlay
Browse files Browse the repository at this point in the history
overlay: Fix root directoy state with extended attributes
  • Loading branch information
openshift-merge-bot[bot] authored Jun 11, 2024
2 parents 0fbaf24 + 18136c5 commit e87d2b4
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 43 deletions.
27 changes: 19 additions & 8 deletions drivers/graphtest/graphtest_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,7 @@ type Driver struct {
refCount int
}

func newDriver(t testing.TB, name string, options []string) *Driver {
root, err := os.MkdirTemp("", "storage-graphtest-")
require.NoError(t, err)
runroot, err := os.MkdirTemp("", "storage-graphtest-")
require.NoError(t, err)

require.NoError(t, os.MkdirAll(root, 0o755))
func newGraphDriver(t testing.TB, name string, options []string, root string, runroot string) graphdriver.Driver {
d, err := graphdriver.GetDriver(name, graphdriver.Options{DriverOptions: options, Root: root, RunRoot: runroot})
if err != nil {
t.Logf("graphdriver: %v\n", err)
Expand All @@ -63,7 +57,17 @@ func newDriver(t testing.TB, name string, options []string) *Driver {
}
t.Fatal(err)
}
return &Driver{d, root, runroot, 1}
return d
}

func newDriver(t testing.TB, name string, options []string) *Driver {
root, err := os.MkdirTemp("", "storage-graphtest-")
require.NoError(t, err)
runroot, err := os.MkdirTemp("", "storage-graphtest-")
require.NoError(t, err)

require.NoError(t, os.MkdirAll(root, 0o755))
return &Driver{newGraphDriver(t, name, options, root, runroot), root, runroot, 1}
}

func cleanup(t testing.TB, d *Driver) {
Expand All @@ -84,6 +88,13 @@ func GetDriver(t testing.TB, name string, options ...string) graphdriver.Driver
return drv
}

func ReconfigureDriver(t testing.TB, name string, options ...string) {
if err := drv.Cleanup(); err != nil {
t.Fatal(err)
}
drv.Driver = newGraphDriver(t, name, options, drv.root, drv.runroot)
}

// PutDriver removes the driver if it is no longer used and updates the reference count.
func PutDriver(t testing.TB) {
if drv == nil {
Expand Down
46 changes: 27 additions & 19 deletions drivers/overlay/overlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -1049,14 +1049,23 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl
if err := idtools.MkdirAllAndChownNew(path.Dir(dir), 0o755, idPair); err != nil {
return err
}

st := idtools.Stat{IDs: idPair, Mode: defaultPerms}

if parent != "" {
parentBase := d.dir(parent)
st, err := system.Stat(filepath.Join(parentBase, "diff"))
if err != nil {
return err
parentDiff := filepath.Join(parentBase, "diff")
if xSt, err := idtools.GetContainersOverrideXattr(parentDiff); err == nil {
st = xSt
} else {
systemSt, err := system.Stat(parentDiff)
if err != nil {
return err
}
st.IDs.UID = int(systemSt.UID())
st.IDs.GID = int(systemSt.GID())
st.Mode = os.FileMode(systemSt.Mode())
}
rootUID = int(st.UID())
rootGID = int(st.GID())
}

if err := fileutils.Lexists(dir); err == nil {
Expand Down Expand Up @@ -1102,22 +1111,21 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl
}
}

perms := defaultPerms
forcedSt := st
if d.options.forceMask != nil {
perms = *d.options.forceMask
forcedSt.IDs = idPair
forcedSt.Mode = *d.options.forceMask
}

if parent != "" {
parentBase := d.dir(parent)
st, err := system.Stat(filepath.Join(parentBase, "diff"))
if err != nil {
return err
}
perms = os.FileMode(st.Mode())
diff := path.Join(dir, "diff")
if err := idtools.MkdirAs(diff, forcedSt.Mode, forcedSt.IDs.UID, forcedSt.IDs.GID); err != nil {
return err
}

if err := idtools.MkdirAs(path.Join(dir, "diff"), perms, rootUID, rootGID); err != nil {
return err
if d.options.forceMask != nil {
if err := idtools.SetContainersOverrideXattr(diff, st); err != nil {
return err
}
}

lid := generateID(idLength)
Expand All @@ -1132,16 +1140,16 @@ func (d *Driver) create(id, parent string, opts *graphdriver.CreateOpts, readOnl
return err
}

if err := idtools.MkdirAs(path.Join(dir, "work"), 0o700, rootUID, rootGID); err != nil {
if err := idtools.MkdirAs(path.Join(dir, "work"), 0o700, forcedSt.IDs.UID, forcedSt.IDs.GID); err != nil {
return err
}
if err := idtools.MkdirAs(path.Join(dir, "merged"), 0o700, rootUID, rootGID); err != nil {
if err := idtools.MkdirAs(path.Join(dir, "merged"), 0o700, forcedSt.IDs.UID, forcedSt.IDs.GID); err != nil {
return err
}

// if no parent directory, create a dummy lower directory and skip writing a "lowers" file
if parent == "" {
return idtools.MkdirAs(path.Join(dir, "empty"), 0o700, rootUID, rootGID)
return idtools.MkdirAs(path.Join(dir, "empty"), 0o700, forcedSt.IDs.UID, forcedSt.IDs.GID)
}

lower, err := d.getLower(parent)
Expand Down
29 changes: 29 additions & 0 deletions drivers/overlay/overlay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
package overlay

import (
"os"
"os/exec"
"testing"

graphdriver "github.com/containers/storage/drivers"
"github.com/containers/storage/drivers/graphtest"
"github.com/containers/storage/pkg/archive"
"github.com/containers/storage/pkg/reexec"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const driverName = "overlay"
Expand All @@ -31,6 +35,31 @@ func skipIfNaive(t *testing.T) {
}
}

// Ensure that a layer created with force_mask will keep the root directory mode
// with user.containers.override_stat. This preserved mode should also be
// inherited by the upper layer, whether force_mask is set or not.
//
// This test is placed before TestOverlaySetup() because it uses driver options
// different from the other tests.
func TestContainersOverlayXattr(t *testing.T) {
path, err := exec.LookPath("fuse-overlayfs")
if err != nil {
t.Skipf("fuse-overlayfs unavailable")
}

driver := graphtest.GetDriver(t, driverName, "force_mask=700", "mount_program="+path)
defer graphtest.PutDriver(t)
require.NoError(t, driver.Create("lower", "", nil))
graphtest.ReconfigureDriver(t, driverName, "mount_program="+path)
require.NoError(t, driver.Create("upper", "lower", nil))

root, err := driver.Get("upper", graphdriver.MountOpts{})
require.NoError(t, err)
fi, err := os.Stat(root)
require.NoError(t, err)
assert.Equal(t, 0o555&os.ModePerm, fi.Mode()&os.ModePerm, root)
}

// This avoids creating a new driver for each test if all tests are run
// Make sure to put new tests between TestOverlaySetup and TestOverlayTeardown
func TestOverlaySetup(t *testing.T) {
Expand Down
15 changes: 10 additions & 5 deletions pkg/archive/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -701,8 +701,11 @@ func createTarFile(path, extractDir string, hdr *tar.Header, reader io.Reader, L
}

if forceMask != nil && (hdr.Typeflag != tar.TypeSymlink || runtime.GOOS == "darwin") {
value := fmt.Sprintf("%d:%d:0%o", hdr.Uid, hdr.Gid, hdrInfo.Mode()&0o7777)
if err := system.Lsetxattr(path, idtools.ContainersOverrideXattr, []byte(value), 0); err != nil {
value := idtools.Stat{
IDs: idtools.IDPair{UID: hdr.Uid, GID: hdr.Gid},
Mode: hdrInfo.Mode() & 0o7777,
}
if err := idtools.SetContainersOverrideXattr(path, value); err != nil {
return err
}
}
Expand Down Expand Up @@ -1114,11 +1117,13 @@ loop:
}

if options.ForceMask != nil {
value := "0:0:0755"
value := idtools.Stat{Mode: 0o755}
if rootHdr != nil {
value = fmt.Sprintf("%d:%d:0%o", rootHdr.Uid, rootHdr.Gid, rootHdr.Mode)
value.IDs.UID = rootHdr.Uid
value.IDs.GID = rootHdr.Gid
value.Mode = os.FileMode(rootHdr.Mode)
}
if err := system.Lsetxattr(dest, idtools.ContainersOverrideXattr, []byte(value), 0); err != nil {
if err := idtools.SetContainersOverrideXattr(dest, value); err != nil {
return err
}
}
Expand Down
9 changes: 6 additions & 3 deletions pkg/chunked/storage_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -1263,9 +1263,12 @@ func (c *chunkedDiffer) ApplyDiff(dest string, options *archive.TarOptions, diff
if options.ForceMask != nil {
uid, gid, mode, err := archive.GetFileOwner(dest)
if err == nil {
value := fmt.Sprintf("%d:%d:0%o", uid, gid, mode)
if err := unix.Setxattr(dest, containersOverrideXattr, []byte(value), 0); err != nil {
return output, &fs.PathError{Op: "setxattr", Path: dest, Err: err}
value := idtools.Stat{
IDs: idtools.IDPair{UID: int(uid), GID: int(gid)},
Mode: os.FileMode(mode),
}
if err := idtools.SetContainersOverrideXattr(dest, value); err != nil {
return output, err
}
}
}
Expand Down
66 changes: 58 additions & 8 deletions pkg/idtools/idtools.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,21 +367,71 @@ func checkChownErr(err error, name string, uid, gid int) error {
return err
}

// Stat contains file states that can be overriden with ContainersOverrideXattr.
type Stat struct {
IDs IDPair
Mode os.FileMode
}

// GetContainersOverrideXattr will get and decode ContainersOverrideXattr.
func GetContainersOverrideXattr(path string) (Stat, error) {
var stat Stat
xstat, err := system.Lgetxattr(path, ContainersOverrideXattr)
if err != nil {
return stat, err
}

attrs := strings.Split(string(xstat), ":")
if len(attrs) != 3 {
return stat, fmt.Errorf("The number of clons in %s does not equal to 3",
ContainersOverrideXattr)
}

value, err := strconv.ParseUint(attrs[0], 10, 32)
if err != nil {
return stat, fmt.Errorf("Failed to parse UID: %w", err)
}

stat.IDs.UID = int(value)

value, err = strconv.ParseUint(attrs[0], 10, 32)
if err != nil {
return stat, fmt.Errorf("Failed to parse GID: %w", err)
}

stat.IDs.GID = int(value)

value, err = strconv.ParseUint(attrs[2], 8, 32)
if err != nil {
return stat, fmt.Errorf("Failed to parse mode: %w", err)
}

stat.Mode = os.FileMode(value)

return stat, nil
}

// SetContainersOverrideXattr will encode and set ContainersOverrideXattr.
func SetContainersOverrideXattr(path string, stat Stat) error {
value := fmt.Sprintf("%d:%d:0%o", stat.IDs.UID, stat.IDs.GID, stat.Mode)
return system.Lsetxattr(path, ContainersOverrideXattr, []byte(value), 0)
}

func SafeChown(name string, uid, gid int) error {
if runtime.GOOS == "darwin" {
var mode uint64 = 0o0700
var mode os.FileMode = 0o0700
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
if err == nil {
attrs := strings.Split(string(xstat), ":")
if len(attrs) == 3 {
val, err := strconv.ParseUint(attrs[2], 8, 32)
if err == nil {
mode = val
mode = os.FileMode(val)
}
}
}
value := fmt.Sprintf("%d:%d:0%o", uid, gid, mode)
if err = system.Lsetxattr(name, ContainersOverrideXattr, []byte(value), 0); err != nil {
value := Stat{IDPair{uid, gid}, mode}
if err = SetContainersOverrideXattr(name, value); err != nil {
return err
}
uid = os.Getuid()
Expand All @@ -397,19 +447,19 @@ func SafeChown(name string, uid, gid int) error {

func SafeLchown(name string, uid, gid int) error {
if runtime.GOOS == "darwin" {
var mode uint64 = 0o0700
var mode os.FileMode = 0o0700
xstat, err := system.Lgetxattr(name, ContainersOverrideXattr)
if err == nil {
attrs := strings.Split(string(xstat), ":")
if len(attrs) == 3 {
val, err := strconv.ParseUint(attrs[2], 8, 32)
if err == nil {
mode = val
mode = os.FileMode(val)
}
}
}
value := fmt.Sprintf("%d:%d:0%o", uid, gid, mode)
if err = system.Lsetxattr(name, ContainersOverrideXattr, []byte(value), 0); err != nil {
value := Stat{IDPair{uid, gid}, mode}
if err = SetContainersOverrideXattr(name, value); err != nil {
return err
}
uid = os.Getuid()
Expand Down

0 comments on commit e87d2b4

Please sign in to comment.