Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pkg/sentry/fsimpl/overlay/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,15 @@ go_library(
"req_file_fd_mutex.go",
"save_restore.go",
],
imports = [
"gvisor.dev/gvisor/pkg/sentry/checkpoint",
"gvisor.dev/gvisor/pkg/sentry/vfs",
],
visibility = ["//pkg/sentry:internal"],
deps = [
"//pkg/abi/linux",
"//pkg/atomicbitops",
"//pkg/cleanup",
"//pkg/context",
"//pkg/errors/linuxerr",
"//pkg/fspath",
Expand Down
58 changes: 56 additions & 2 deletions pkg/sentry/fsimpl/overlay/copy_up.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,18 @@ func (d *dentry) copyUpMaybeSyntheticMountpointLocked(ctx context.Context, forSy
cleanupUndoCopyUp()
return err
}
acl := d.accessACL.Load()
newACL, newMode, err := newFD.SetPosixACL(ctx, vfs.AccessACL, acl, false /* clearSGID */)
if err == nil {
d.accessACL.Store(newACL)
d.mode.Store(uint32(newMode))
} else if acl == nil && linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
// Tolerate EOPNOTSUPP for nil ACLs since if the upper filesystem
// doesn't support ACLs, all files have an implicit nil ACL
} else {
cleanupUndoCopyUp()
return err
}
d.upperVD = newFD.VirtualDentry()
d.upperVD.IncRef()

Expand All @@ -207,6 +219,29 @@ func (d *dentry) copyUpMaybeSyntheticMountpointLocked(ctx context.Context, forSy
cleanupUndoCopyUp()
return err
}
acl := d.accessACL.Load()
newACL, newMode, err := vfsObj.SetPosixACLAt(ctx, d.fs.creds, &newpop, vfs.AccessACL, acl, false /* clearSGID */)
if err == nil {
d.accessACL.Store(newACL)
d.mode.Store(uint32(newMode))
} else if acl == nil && linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
// Tolerate EOPNOTSUPP for nil ACLs since if the upper filesystem
// doesn't support ACLs, all files have an implicit nil ACL
} else {
cleanupUndoCopyUp()
return err
}
defaultACL := d.defaultACL.Load()
newDefaultACL, _, err := vfsObj.SetPosixACLAt(ctx, d.fs.creds, &newpop, vfs.DefaultACL, defaultACL, false /* clearSGID */)
if err == nil {
d.defaultACL.Store(newDefaultACL)
} else if defaultACL == nil && linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
// Tolerate EOPNOTSUPP for nil ACLs since if the upper filesystem
// doesn't support ACLs, all files have an implicit nil ACL
} else {
cleanupUndoCopyUp()
return err
}
upperVD, err := vfsObj.GetDentryAt(ctx, d.fs.creds, &newpop, &vfs.GetDentryOptions{})
if err != nil {
cleanupUndoCopyUp()
Expand Down Expand Up @@ -236,6 +271,7 @@ func (d *dentry) copyUpMaybeSyntheticMountpointLocked(ctx context.Context, forSy
cleanupUndoCopyUp()
return err
}
// Skip POSIX ACLs since symlinks cannot have them.
upperVD, err := vfsObj.GetDentryAt(ctx, d.fs.creds, &newpop, &vfs.GetDentryOptions{})
if err != nil {
cleanupUndoCopyUp()
Expand All @@ -254,8 +290,9 @@ func (d *dentry) copyUpMaybeSyntheticMountpointLocked(ctx context.Context, forSy
}
if err := vfsObj.SetStatAt(ctx, d.fs.creds, &newpop, &vfs.SetStatOptions{
Stat: linux.Statx{
Mask: linux.STATX_UID | linux.STATX_GID | oldStat.Mask&timestampsMask,
Mask: linux.STATX_MODE | linux.STATX_UID | linux.STATX_GID | oldStat.Mask&timestampsMask,
// d.uid and d.gid can be read because d.copyMu is locked.
Mode: uint16(d.mode.RacyLoad()),
UID: d.uid.RacyLoad(),
GID: d.gid.RacyLoad(),
Atime: oldStat.Atime,
Expand All @@ -265,6 +302,18 @@ func (d *dentry) copyUpMaybeSyntheticMountpointLocked(ctx context.Context, forSy
cleanupUndoCopyUp()
return err
}
acl := d.accessACL.Load()
newACL, newMode, err := vfsObj.SetPosixACLAt(ctx, d.fs.creds, &newpop, vfs.AccessACL, acl, false /* clearSGID */)
if err == nil {
d.accessACL.Store(newACL)
d.mode.Store(uint32(newMode))
} else if acl == nil && linuxerr.Equals(linuxerr.EOPNOTSUPP, err) {
// Tolerate EOPNOTSUPP for nil ACLs since if the upper filesystem
// doesn't support ACLs, all files have an implicit nil ACL
} else {
cleanupUndoCopyUp()
return err
}
upperVD, err := vfsObj.GetDentryAt(ctx, d.fs.creds, &newpop, &vfs.GetDentryOptions{})
if err != nil {
cleanupUndoCopyUp()
Expand Down Expand Up @@ -375,7 +424,7 @@ func (d *dentry) copyUpMaybeSyntheticMountpointLocked(ctx context.Context, forSy
// abort the copy-up. Loosely analogous to Linux's
// fs/overlayfs/util.c:ovl_must_copy_xattr(). Here are the differences:
// - Linux includes "system.posix_acl_access" and "system.posix_acl_default".
// As of writing, these are not supported in gVisor and so are excluded.
// In gVisor, we handle these separately.
// - Linux includes all "security.*" xattrs. gVisor only supports
// "security.capability" and so only that is included here.
func mustCopyXattr(name string) bool {
Expand Down Expand Up @@ -407,6 +456,11 @@ func (d *dentry) copyXattrsLocked(ctx context.Context) error {
continue
}

// Skip POSIX ACLs, which are handled separately.
if name == linux.XATTR_NAME_POSIX_ACL_ACCESS || name == linux.XATTR_NAME_POSIX_ACL_DEFAULT {
continue
}

value, err := vfsObj.GetXattrAt(ctx, d.fs.creds, lowerPop, &vfs.GetXattrOptions{Name: name, Size: 0})
if err != nil {
ctx.Infof("failed to copy up %q xattr because GetXattrAt failed: %v", name, err)
Expand Down
132 changes: 121 additions & 11 deletions pkg/sentry/fsimpl/overlay/filesystem.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,10 +226,11 @@ func (fs *filesystem) lookupLocked(ctx context.Context, parent *dentry, name str
// the topmost layer on which the file exists.
mask |= linux.STATX_MODE | linux.STATX_UID | linux.STATX_GID | linux.STATX_INO
}
stat, err := vfsObj.StatAt(ctx, fs.creds, &vfs.PathOperation{
childPop := &vfs.PathOperation{
Root: childVD,
Start: childVD,
}, &vfs.StatOptions{
}
stat, err := vfsObj.StatAt(ctx, fs.creds, childPop, &vfs.StatOptions{
Mask: mask,
})
if err != nil {
Expand Down Expand Up @@ -273,7 +274,22 @@ func (fs *filesystem) lookupLocked(ctx context.Context, parent *dentry, name str
} else {
topLookupLayer = lookupLayerLower
}

// Fetch POSIX ACLs.
acl, err := vfsObj.GetPosixACLAt(ctx, fs.creds, childPop, vfs.AccessACL)
if err != nil {
lookupErr = err
return false
}
defaultACL, err := vfsObj.GetPosixACLAt(ctx, fs.creds, childPop, vfs.DefaultACL)
if err != nil {
lookupErr = err
return false
}

child.mode = atomicbitops.FromUint32(uint32(stat.Mode))
child.accessACL.Store(acl)
child.defaultACL.Store(defaultACL)
child.uid = atomicbitops.FromUint32(stat.UID)
child.gid = atomicbitops.FromUint32(stat.GID)
child.devMajor = atomicbitops.FromUint32(stat.DevMajor)
Expand Down Expand Up @@ -1506,7 +1522,7 @@ func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts
// Precondition: d.fs.renameMu must be held for reading.
func (d *dentry) setStatLocked(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error {
mode := linux.FileMode(d.mode.Load())
if err := vfs.CheckSetStat(ctx, rp.Credentials(), &opts, mode, nil, auth.KUID(d.uid.Load()), auth.KGID(d.gid.Load())); err != nil {
if err := vfs.CheckSetStat(ctx, rp.Credentials(), &opts, mode, d.accessACL.Load(), auth.KUID(d.uid.Load()), auth.KGID(d.gid.Load())); err != nil {
return err
}
mnt := rp.Mount()
Expand Down Expand Up @@ -1764,6 +1780,13 @@ func (fs *filesystem) GetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
}

func (fs *filesystem) getXattr(ctx context.Context, d *dentry, creds *auth.Credentials, opts *vfs.GetXattrOptions) (string, error) {
// Handle POSIX access ACL xattr
if strings.HasPrefix(opts.Name, linux.XATTR_SYSTEM_PREFIX) {
// Handle POSIX ACL xattrs
xattr, err := vfs.ACLGetXattr(creds, opts, linux.FileMode(d.mode.Load()), d.accessACL.Load(), d.defaultACL.Load())
return xattr, err
}

if err := d.checkXattrPermissions(creds, opts.Name, vfs.MayRead); err != nil {
return "", err
}
Expand Down Expand Up @@ -1800,8 +1823,20 @@ func (fs *filesystem) SetXattrAt(ctx context.Context, rp *vfs.ResolvingPath, opt
return nil
}

// Precondition: fs.renameMu must be locked.
// Precondition: fs.renameMu must be locked, d.copyMu must be unlocked.
func (fs *filesystem) setXattrLocked(ctx context.Context, d *dentry, mnt *vfs.Mount, creds *auth.Credentials, opts *vfs.SetXattrOptions) error {
// Handle POSIX ACLs separately
if strings.HasPrefix(opts.Name, linux.XATTR_SYSTEM_PREFIX) {
acl, aclType, err := vfs.ACLSetXattr(creds, opts, linux.FileMode(d.mode.Load()), auth.KUID(d.uid.Load()))
if err != nil {
return err
}

// Set the appropriate POSIX ACL
_, _, err = fs.setPosixACLLocked(ctx, d, creds, mnt, aclType, acl, true /* clearSGID */)
return err
}

if err := d.checkXattrPermissions(creds, opts.Name, vfs.MayWrite); err != nil {
return err
}
Expand Down Expand Up @@ -1844,8 +1879,19 @@ func (fs *filesystem) RemoveXattrAt(ctx context.Context, rp *vfs.ResolvingPath,
return nil
}

// Precondition: fs.renameMu must be locked.
// Precondition: fs.renameMu must be locked, d.copyMu must be unlocked.
func (fs *filesystem) removeXattrLocked(ctx context.Context, d *dentry, mnt *vfs.Mount, creds *auth.Credentials, name string) error {
if strings.HasPrefix(name, linux.XATTR_SYSTEM_PREFIX) {
aclType, err := vfs.ACLRemoveXattr(creds, name, linux.FileMode(d.mode.Load()), auth.KUID(d.uid.Load()))
if err != nil {
return err
}

// Clear the appropriate POSIX ACL
_, _, err = fs.setPosixACLLocked(ctx, d, creds, mnt, aclType, nil, true /* clearSGID */)
return err
}

if err := d.checkXattrPermissions(creds, name, vfs.MayWrite); err != nil {
return err
}
Expand Down Expand Up @@ -1873,22 +1919,86 @@ func (fs *filesystem) GetPosixACLAt(ctx context.Context, rp *vfs.ResolvingPath,
var ds *[]*dentry
fs.renameMu.RLock()
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
// overlayfs does not currently support POSIX ACLs.
_, err := fs.resolveLocked(ctx, rp, &ds)
return nil, err
d, err := fs.resolveLocked(ctx, rp, &ds)
if err != nil {
return nil, err
}
return fs.getPosixACLLocked(ctx, d, t)
}

func (fs *filesystem) getPosixACLLocked(ctx context.Context, d *dentry, t vfs.ACLType) (*vfs.PosixACL, error) {
switch t {
case vfs.AccessACL:
return d.accessACL.Load(), nil
case vfs.DefaultACL:
return d.defaultACL.Load(), nil
default:
return nil, linuxerr.EINVAL
}
}

// SetPosixACLAt implements vfs.FilesystemImpl.SetPosixACLAt.
func (fs *filesystem) SetPosixACLAt(ctx context.Context, rp *vfs.ResolvingPath, t vfs.ACLType, acl *vfs.PosixACL, clearSGID bool) (*vfs.PosixACL, linux.FileMode, error) {
var ds *[]*dentry
fs.renameMu.RLock()
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
_, err := fs.resolveLocked(ctx, rp, &ds)
d, err := fs.resolveLocked(ctx, rp, &ds)
if err != nil {
return nil, 0, err
}
newACL, mode, err := fs.setPosixACLLocked(ctx, d, rp.Credentials(), rp.Mount(), t, acl, clearSGID)
return newACL, mode, err
}

// Precondition: fs.renameMu must be locked, d.copyMu must be unlocked.
func (fs *filesystem) setPosixACLLocked(ctx context.Context, d *dentry, creds *auth.Credentials, mnt *vfs.Mount, t vfs.ACLType, acl *vfs.PosixACL, clearSGID bool) (*vfs.PosixACL, linux.FileMode, error) {
// Skip ACL update when clearing an already-missing ACL (to prevent unnecessary copy-up)
oldACL, err := fs.getPosixACLLocked(ctx, d, t)
if err != nil {
return nil, 0, err
}
// Take copyMu to check d.upperVD. Technically d could be copied up between here and the later
// call to d.copyUpLocked(), but this would have no effect.
d.copyMu.RLock()
copiedUp := d.upperVD.Ok()
d.copyMu.RUnlock()
if acl == nil && oldACL == nil && !copiedUp {
mode := linux.FileMode(d.mode.Load())
if t == vfs.DefaultACL && !mode.IsDir() {
return nil, 0, nil
}
// ENODATA to match Linux.
return nil, 0, linuxerr.ENODATA
}

if err := mnt.CheckBeginWrite(); err != nil {
return nil, 0, err
}
defer mnt.EndWrite()
if err := d.copyUpLocked(ctx); err != nil {
return nil, 0, err
}
vfsObj := d.fs.vfsfs.VirtualFilesystem()
upperPop := &vfs.PathOperation{Root: d.upperVD, Start: d.upperVD}

// First, set the ACL on the underlying filesystem
d.copyMu.Lock()
defer d.copyMu.Unlock()
newACL, mode, err := vfsObj.SetPosixACLAt(ctx, creds, upperPop, t, acl, clearSGID)
if err != nil {
return nil, 0, err
}
// overlayfs does not currently support POSIX ACLs.
return nil, 0, linuxerr.EOPNOTSUPP

// Next, update the ACL in our dentry
switch t {
case vfs.AccessACL:
d.accessACL.Store(newACL)
d.mode.Store(uint32(mode))
case vfs.DefaultACL:
d.defaultACL.Store(newACL)
}

return newACL, mode, nil
}

// PrependPath implements vfs.FilesystemImpl.PrependPath.
Expand Down
Loading
Loading