mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Add a skeleton for verity file system
PiperOrigin-RevId: 327123477
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
load("//tools:defs.bzl", "go_library")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
go_library(
|
||||
name = "verity",
|
||||
srcs = [
|
||||
"filesystem.go",
|
||||
"verity.go",
|
||||
],
|
||||
visibility = ["//pkg/sentry:internal"],
|
||||
deps = [
|
||||
"//pkg/abi/linux",
|
||||
"//pkg/context",
|
||||
"//pkg/fspath",
|
||||
"//pkg/sentry/fs/lock",
|
||||
"//pkg/sentry/kernel/auth",
|
||||
"//pkg/sentry/socket/unix/transport",
|
||||
"//pkg/sentry/vfs",
|
||||
"//pkg/sync",
|
||||
"//pkg/syserror",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,333 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package verity
|
||||
|
||||
import (
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
"gvisor.dev/gvisor/pkg/fspath"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/socket/unix/transport"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
// Sync implements vfs.FilesystemImpl.Sync.
|
||||
func (fs *filesystem) Sync(ctx context.Context) error {
|
||||
// All files should be read-only.
|
||||
return nil
|
||||
}
|
||||
|
||||
var dentrySlicePool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
ds := make([]*dentry, 0, 4) // arbitrary non-zero initial capacity
|
||||
return &ds
|
||||
},
|
||||
}
|
||||
|
||||
func appendDentry(ds *[]*dentry, d *dentry) *[]*dentry {
|
||||
if ds == nil {
|
||||
ds = dentrySlicePool.Get().(*[]*dentry)
|
||||
}
|
||||
*ds = append(*ds, d)
|
||||
return ds
|
||||
}
|
||||
|
||||
// Preconditions: ds != nil.
|
||||
func putDentrySlice(ds *[]*dentry) {
|
||||
// Allow dentries to be GC'd.
|
||||
for i := range *ds {
|
||||
(*ds)[i] = nil
|
||||
}
|
||||
*ds = (*ds)[:0]
|
||||
dentrySlicePool.Put(ds)
|
||||
}
|
||||
|
||||
// renameMuRUnlockAndCheckDrop calls fs.renameMu.RUnlock(), then calls
|
||||
// dentry.checkDropLocked on all dentries in *ds with fs.renameMu locked for
|
||||
// writing.
|
||||
//
|
||||
// ds is a pointer-to-pointer since defer evaluates its arguments immediately,
|
||||
// but dentry slices are allocated lazily, and it's much easier to say "defer
|
||||
// fs.renameMuRUnlockAndCheckDrop(&ds)" than "defer func() {
|
||||
// fs.renameMuRUnlockAndCheckDrop(ds) }()" to work around this.
|
||||
func (fs *filesystem) renameMuRUnlockAndCheckDrop(ctx context.Context, ds **[]*dentry) {
|
||||
fs.renameMu.RUnlock()
|
||||
if *ds == nil {
|
||||
return
|
||||
}
|
||||
if len(**ds) != 0 {
|
||||
fs.renameMu.Lock()
|
||||
for _, d := range **ds {
|
||||
d.checkDropLocked(ctx)
|
||||
}
|
||||
fs.renameMu.Unlock()
|
||||
}
|
||||
putDentrySlice(*ds)
|
||||
}
|
||||
|
||||
func (fs *filesystem) renameMuUnlockAndCheckDrop(ctx context.Context, ds **[]*dentry) {
|
||||
if *ds == nil {
|
||||
fs.renameMu.Unlock()
|
||||
return
|
||||
}
|
||||
for _, d := range **ds {
|
||||
d.checkDropLocked(ctx)
|
||||
}
|
||||
fs.renameMu.Unlock()
|
||||
putDentrySlice(*ds)
|
||||
}
|
||||
|
||||
// resolveLocked resolves rp to an existing file.
|
||||
func (fs *filesystem) resolveLocked(ctx context.Context, rp *vfs.ResolvingPath, ds **[]*dentry) (*dentry, error) {
|
||||
// TODO(b/159261227): Implement resolveLocked.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// walkParentDirLocked resolves all but the last path component of rp to an
|
||||
// existing directory, starting from the given directory (which is usually
|
||||
// rp.Start().Impl().(*dentry)). It does not check that the returned directory
|
||||
// is searchable by the provider of rp.
|
||||
//
|
||||
// Preconditions: fs.renameMu must be locked. !rp.Done().
|
||||
func (fs *filesystem) walkParentDirLocked(ctx context.Context, rp *vfs.ResolvingPath, d *dentry, ds **[]*dentry) (*dentry, error) {
|
||||
// TODO(b/159261227): Implement walkParentDirLocked.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// AccessAt implements vfs.Filesystem.Impl.AccessAt.
|
||||
func (fs *filesystem) AccessAt(ctx context.Context, rp *vfs.ResolvingPath, creds *auth.Credentials, ats vfs.AccessTypes) error {
|
||||
// Verity file system is read-only.
|
||||
if ats&vfs.MayWrite != 0 {
|
||||
return syserror.EROFS
|
||||
}
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return d.checkPermissions(creds, ats)
|
||||
}
|
||||
|
||||
// GetDentryAt implements vfs.FilesystemImpl.GetDentryAt.
|
||||
func (fs *filesystem) GetDentryAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetDentryOptions) (*vfs.Dentry, error) {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if opts.CheckSearchable {
|
||||
if !d.isDir() {
|
||||
return nil, syserror.ENOTDIR
|
||||
}
|
||||
if err := d.checkPermissions(rp.Credentials(), vfs.MayExec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d.IncRef()
|
||||
return &d.vfsd, nil
|
||||
}
|
||||
|
||||
// GetParentDentryAt implements vfs.FilesystemImpl.GetParentDentryAt.
|
||||
func (fs *filesystem) GetParentDentryAt(ctx context.Context, rp *vfs.ResolvingPath) (*vfs.Dentry, error) {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
start := rp.Start().Impl().(*dentry)
|
||||
d, err := fs.walkParentDirLocked(ctx, rp, start, &ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.IncRef()
|
||||
return &d.vfsd, nil
|
||||
}
|
||||
|
||||
// LinkAt implements vfs.FilesystemImpl.LinkAt.
|
||||
func (fs *filesystem) LinkAt(ctx context.Context, rp *vfs.ResolvingPath, vd vfs.VirtualDentry) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// MkdirAt implements vfs.FilesystemImpl.MkdirAt.
|
||||
func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MkdirOptions) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// MknodAt implements vfs.FilesystemImpl.MknodAt.
|
||||
func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// OpenAt implements vfs.FilesystemImpl.OpenAt.
|
||||
func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.OpenOptions) (*vfs.FileDescription, error) {
|
||||
//TODO(b/159261227): Implement OpenAt.
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ReadlinkAt implements vfs.FilesystemImpl.ReadlinkAt.
|
||||
func (fs *filesystem) ReadlinkAt(ctx context.Context, rp *vfs.ResolvingPath) (string, error) {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
//TODO(b/162787271): Provide integrity check for ReadlinkAt.
|
||||
return fs.vfsfs.VirtualFilesystem().ReadlinkAt(ctx, d.fs.creds, &vfs.PathOperation{
|
||||
Root: d.lowerVD,
|
||||
Start: d.lowerVD,
|
||||
})
|
||||
}
|
||||
|
||||
// RenameAt implements vfs.FilesystemImpl.RenameAt.
|
||||
func (fs *filesystem) RenameAt(ctx context.Context, rp *vfs.ResolvingPath, oldParentVD vfs.VirtualDentry, oldName string, opts vfs.RenameOptions) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// RmdirAt implements vfs.FilesystemImpl.RmdirAt.
|
||||
func (fs *filesystem) RmdirAt(ctx context.Context, rp *vfs.ResolvingPath) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// SetStatAt implements vfs.FilesystemImpl.SetStatAt.
|
||||
func (fs *filesystem) SetStatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetStatOptions) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// StatAt implements vfs.FilesystemImpl.StatAt.
|
||||
func (fs *filesystem) StatAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.StatOptions) (linux.Statx, error) {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
return linux.Statx{}, err
|
||||
}
|
||||
|
||||
var stat linux.Statx
|
||||
stat, err = fs.vfsfs.VirtualFilesystem().StatAt(ctx, fs.creds, &vfs.PathOperation{
|
||||
Root: d.lowerVD,
|
||||
Start: d.lowerVD,
|
||||
}, &opts)
|
||||
if err != nil {
|
||||
return linux.Statx{}, err
|
||||
}
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
// StatFSAt implements vfs.FilesystemImpl.StatFSAt.
|
||||
func (fs *filesystem) StatFSAt(ctx context.Context, rp *vfs.ResolvingPath) (linux.Statfs, error) {
|
||||
// TODO(b/159261227): Implement StatFSAt.
|
||||
return linux.Statfs{}, nil
|
||||
}
|
||||
|
||||
// SymlinkAt implements vfs.FilesystemImpl.SymlinkAt.
|
||||
func (fs *filesystem) SymlinkAt(ctx context.Context, rp *vfs.ResolvingPath, target string) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// UnlinkAt implements vfs.FilesystemImpl.UnlinkAt.
|
||||
func (fs *filesystem) UnlinkAt(ctx context.Context, rp *vfs.ResolvingPath) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// BoundEndpointAt implements FilesystemImpl.BoundEndpointAt.
|
||||
func (fs *filesystem) BoundEndpointAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.BoundEndpointOptions) (transport.BoundEndpoint, error) {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
if _, err := fs.resolveLocked(ctx, rp, &ds); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, syserror.ECONNREFUSED
|
||||
}
|
||||
|
||||
// ListxattrAt implements vfs.FilesystemImpl.ListxattrAt.
|
||||
func (fs *filesystem) ListxattrAt(ctx context.Context, rp *vfs.ResolvingPath, size uint64) ([]string, error) {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lowerVD := d.lowerVD
|
||||
return fs.vfsfs.VirtualFilesystem().ListxattrAt(ctx, d.fs.creds, &vfs.PathOperation{
|
||||
Root: lowerVD,
|
||||
Start: lowerVD,
|
||||
}, size)
|
||||
}
|
||||
|
||||
// GetxattrAt implements vfs.FilesystemImpl.GetxattrAt.
|
||||
func (fs *filesystem) GetxattrAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.GetxattrOptions) (string, error) {
|
||||
var ds *[]*dentry
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMuRUnlockAndCheckDrop(ctx, &ds)
|
||||
d, err := fs.resolveLocked(ctx, rp, &ds)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
lowerVD := d.lowerVD
|
||||
return fs.vfsfs.VirtualFilesystem().GetxattrAt(ctx, d.fs.creds, &vfs.PathOperation{
|
||||
Root: lowerVD,
|
||||
Start: lowerVD,
|
||||
}, &opts)
|
||||
}
|
||||
|
||||
// SetxattrAt implements vfs.FilesystemImpl.SetxattrAt.
|
||||
func (fs *filesystem) SetxattrAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.SetxattrOptions) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// RemovexattrAt implements vfs.FilesystemImpl.RemovexattrAt.
|
||||
func (fs *filesystem) RemovexattrAt(ctx context.Context, rp *vfs.ResolvingPath, name string) error {
|
||||
// Verity file system is read-only.
|
||||
return syserror.EROFS
|
||||
}
|
||||
|
||||
// PrependPath implements vfs.FilesystemImpl.PrependPath.
|
||||
func (fs *filesystem) PrependPath(ctx context.Context, vfsroot, vd vfs.VirtualDentry, b *fspath.Builder) error {
|
||||
fs.renameMu.RLock()
|
||||
defer fs.renameMu.RUnlock()
|
||||
mnt := vd.Mount()
|
||||
d := vd.Dentry().Impl().(*dentry)
|
||||
for {
|
||||
if mnt == vfsroot.Mount() && &d.vfsd == vfsroot.Dentry() {
|
||||
return vfs.PrependPathAtVFSRootError{}
|
||||
}
|
||||
if &d.vfsd == mnt.Root() {
|
||||
return nil
|
||||
}
|
||||
if d.parent == nil {
|
||||
return vfs.PrependPathAtNonMountRootError{}
|
||||
}
|
||||
b.PrependComponent(d.name)
|
||||
d = d.parent
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,355 @@
|
||||
// Copyright 2020 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package verity provides a filesystem implementation that is a wrapper of
|
||||
// another file system.
|
||||
// The verity file system provides integrity check for the underlying file
|
||||
// system by providing verification for path traversals and each read.
|
||||
// The verity file system is read-only, except for one case: when
|
||||
// allowRuntimeEnable is true, additional Merkle files can be generated using
|
||||
// the FS_IOC_ENABLE_VERITY ioctl.
|
||||
package verity
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||
"gvisor.dev/gvisor/pkg/context"
|
||||
fslock "gvisor.dev/gvisor/pkg/sentry/fs/lock"
|
||||
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||
"gvisor.dev/gvisor/pkg/sync"
|
||||
"gvisor.dev/gvisor/pkg/syserror"
|
||||
)
|
||||
|
||||
// Name is the default filesystem name.
|
||||
const Name = "verity"
|
||||
|
||||
// testOnlyDebugging allows verity file system to return error instead of
|
||||
// crashing the application when a malicious action is detected. This should
|
||||
// only be set for tests.
|
||||
var testOnlyDebugging bool
|
||||
|
||||
// FilesystemType implements vfs.FilesystemType.
|
||||
type FilesystemType struct{}
|
||||
|
||||
// filesystem implements vfs.FilesystemImpl.
|
||||
type filesystem struct {
|
||||
vfsfs vfs.Filesystem
|
||||
|
||||
// creds is a copy of the filesystem's creator's credentials, which are
|
||||
// used for accesses to the underlying file system. creds is immutable.
|
||||
creds *auth.Credentials
|
||||
|
||||
// allowRuntimeEnable is true if using ioctl with FS_IOC_ENABLE_VERITY
|
||||
// to build Merkle trees in the verity file system is allowed. If this
|
||||
// is false, no new Merkle trees can be built, and only the files that
|
||||
// had Merkle trees before startup (e.g. from a host filesystem mounted
|
||||
// with gofer fs) can be verified.
|
||||
allowRuntimeEnable bool
|
||||
|
||||
// lowerMount is the underlying file system mount.
|
||||
lowerMount *vfs.Mount
|
||||
|
||||
// rootDentry is the mount root Dentry for this file system, which
|
||||
// stores the root hash of the whole file system in bytes.
|
||||
rootDentry *dentry
|
||||
|
||||
// renameMu synchronizes renaming with non-renaming operations in order
|
||||
// to ensure consistent lock ordering between dentry.dirMu in different
|
||||
// dentries.
|
||||
renameMu sync.RWMutex
|
||||
}
|
||||
|
||||
// InternalFilesystemOptions may be passed as
|
||||
// vfs.GetFilesystemOptions.InternalData to FilesystemType.GetFilesystem.
|
||||
type InternalFilesystemOptions struct {
|
||||
// RootMerkleFileName is the name of the verity root Merkle tree file.
|
||||
RootMerkleFileName string
|
||||
|
||||
// LowerName is the name of the filesystem wrapped by verity fs.
|
||||
LowerName string
|
||||
|
||||
// RootHash is the root hash of the overall verity file system.
|
||||
RootHash []byte
|
||||
|
||||
// AllowRuntimeEnable specifies whether the verity file system allows
|
||||
// enabling verification for files (i.e. building Merkle trees) during
|
||||
// runtime.
|
||||
AllowRuntimeEnable bool
|
||||
|
||||
// LowerGetFSOptions is the file system option for the lower layer file
|
||||
// system wrapped by verity file system.
|
||||
LowerGetFSOptions vfs.GetFilesystemOptions
|
||||
|
||||
// TestOnlyDebugging allows verity file system to return error instead
|
||||
// of crashing the application when a malicious action is detected. This
|
||||
// should only be set for tests.
|
||||
TestOnlyDebugging bool
|
||||
}
|
||||
|
||||
// Name implements vfs.FilesystemType.Name.
|
||||
func (FilesystemType) Name() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
// GetFilesystem implements vfs.FilesystemType.GetFilesystem.
|
||||
func (fstype FilesystemType) GetFilesystem(ctx context.Context, vfsObj *vfs.VirtualFilesystem, creds *auth.Credentials, source string, opts vfs.GetFilesystemOptions) (*vfs.Filesystem, *vfs.Dentry, error) {
|
||||
//TODO(b/159261227): Implement GetFilesystem.
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
// Release implements vfs.FilesystemImpl.Release.
|
||||
func (fs *filesystem) Release(ctx context.Context) {
|
||||
fs.lowerMount.DecRef(ctx)
|
||||
}
|
||||
|
||||
// dentry implements vfs.DentryImpl.
|
||||
type dentry struct {
|
||||
vfsd vfs.Dentry
|
||||
|
||||
refs int64
|
||||
|
||||
// fs is the owning filesystem. fs is immutable.
|
||||
fs *filesystem
|
||||
|
||||
// mode, uid and gid are the file mode, owner, and group of the file in
|
||||
// the underlying file system.
|
||||
mode uint32
|
||||
uid uint32
|
||||
gid uint32
|
||||
|
||||
// parent is the dentry corresponding to this dentry's parent directory.
|
||||
// name is this dentry's name in parent. If this dentry is a filesystem
|
||||
// root, parent is nil and name is the empty string. parent and name are
|
||||
// protected by fs.renameMu.
|
||||
parent *dentry
|
||||
name string
|
||||
|
||||
// If this dentry represents a directory, children maps the names of
|
||||
// children for which dentries have been instantiated to those dentries,
|
||||
// and dirents (if not nil) is a cache of dirents as returned by
|
||||
// directoryFDs representing this directory. children is protected by
|
||||
// dirMu.
|
||||
dirMu sync.Mutex
|
||||
children map[string]*dentry
|
||||
|
||||
// lowerVD is the VirtualDentry in the underlying file system.
|
||||
lowerVD vfs.VirtualDentry
|
||||
|
||||
// lowerMerkleVD is the VirtualDentry of the corresponding Merkle tree
|
||||
// in the underlying file system.
|
||||
lowerMerkleVD vfs.VirtualDentry
|
||||
|
||||
// rootHash is the rootHash for the current file or directory.
|
||||
rootHash []byte
|
||||
}
|
||||
|
||||
// newDentry creates a new dentry representing the given verity file. The
|
||||
// dentry initially has no references; it is the caller's responsibility to set
|
||||
// the dentry's reference count and/or call dentry.destroy() as appropriate.
|
||||
// The dentry is initially invalid in that it contains no underlying dentry;
|
||||
// the caller is responsible for setting them.
|
||||
func (fs *filesystem) newDentry() *dentry {
|
||||
d := &dentry{
|
||||
fs: fs,
|
||||
}
|
||||
d.vfsd.Init(d)
|
||||
return d
|
||||
}
|
||||
|
||||
// IncRef implements vfs.DentryImpl.IncRef.
|
||||
func (d *dentry) IncRef() {
|
||||
atomic.AddInt64(&d.refs, 1)
|
||||
}
|
||||
|
||||
// TryIncRef implements vfs.DentryImpl.TryIncRef.
|
||||
func (d *dentry) TryIncRef() bool {
|
||||
for {
|
||||
refs := atomic.LoadInt64(&d.refs)
|
||||
if refs <= 0 {
|
||||
return false
|
||||
}
|
||||
if atomic.CompareAndSwapInt64(&d.refs, refs, refs+1) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DecRef implements vfs.DentryImpl.DecRef.
|
||||
func (d *dentry) DecRef(ctx context.Context) {
|
||||
if refs := atomic.AddInt64(&d.refs, -1); refs == 0 {
|
||||
d.fs.renameMu.Lock()
|
||||
d.checkDropLocked(ctx)
|
||||
d.fs.renameMu.Unlock()
|
||||
} else if refs < 0 {
|
||||
panic("verity.dentry.DecRef() called without holding a reference")
|
||||
}
|
||||
}
|
||||
|
||||
// checkDropLocked should be called after d's reference count becomes 0 or it
|
||||
// becomes deleted.
|
||||
func (d *dentry) checkDropLocked(ctx context.Context) {
|
||||
// Dentries with a positive reference count must be retained. Dentries
|
||||
// with a negative reference count have already been destroyed.
|
||||
if atomic.LoadInt64(&d.refs) != 0 {
|
||||
return
|
||||
}
|
||||
// Refs is still zero; destroy it.
|
||||
d.destroyLocked(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// destroyLocked destroys the dentry.
|
||||
//
|
||||
// Preconditions: d.fs.renameMu must be locked for writing. d.refs == 0.
|
||||
func (d *dentry) destroyLocked(ctx context.Context) {
|
||||
switch atomic.LoadInt64(&d.refs) {
|
||||
case 0:
|
||||
// Mark the dentry destroyed.
|
||||
atomic.StoreInt64(&d.refs, -1)
|
||||
case -1:
|
||||
panic("verity.dentry.destroyLocked() called on already destroyed dentry")
|
||||
default:
|
||||
panic("verity.dentry.destroyLocked() called with references on the dentry")
|
||||
}
|
||||
|
||||
if d.lowerVD.Ok() {
|
||||
d.lowerVD.DecRef(ctx)
|
||||
}
|
||||
|
||||
if d.lowerMerkleVD.Ok() {
|
||||
d.lowerMerkleVD.DecRef(ctx)
|
||||
}
|
||||
|
||||
if d.parent != nil {
|
||||
d.parent.dirMu.Lock()
|
||||
if !d.vfsd.IsDead() {
|
||||
delete(d.parent.children, d.name)
|
||||
}
|
||||
d.parent.dirMu.Unlock()
|
||||
if refs := atomic.AddInt64(&d.parent.refs, -1); refs == 0 {
|
||||
d.parent.checkDropLocked(ctx)
|
||||
} else if refs < 0 {
|
||||
panic("verity.dentry.DecRef() called without holding a reference")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InotifyWithParent implements vfs.DentryImpl.InotifyWithParent.
|
||||
func (d *dentry) InotifyWithParent(ctx context.Context, events, cookie uint32, et vfs.EventType) {
|
||||
//TODO(b/159261227): Implement InotifyWithParent.
|
||||
}
|
||||
|
||||
// Watches implements vfs.DentryImpl.Watches.
|
||||
func (d *dentry) Watches() *vfs.Watches {
|
||||
//TODO(b/159261227): Implement Watches.
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnZeroWatches implements vfs.DentryImpl.OnZeroWatches.
|
||||
func (d *dentry) OnZeroWatches(context.Context) {
|
||||
//TODO(b/159261227): Implement OnZeroWatches.
|
||||
}
|
||||
|
||||
func (d *dentry) isSymlink() bool {
|
||||
return atomic.LoadUint32(&d.mode)&linux.S_IFMT == linux.S_IFLNK
|
||||
}
|
||||
|
||||
func (d *dentry) isDir() bool {
|
||||
return atomic.LoadUint32(&d.mode)&linux.S_IFMT == linux.S_IFDIR
|
||||
}
|
||||
|
||||
func (d *dentry) checkPermissions(creds *auth.Credentials, ats vfs.AccessTypes) error {
|
||||
return vfs.GenericCheckPermissions(creds, ats, linux.FileMode(atomic.LoadUint32(&d.mode)), auth.KUID(atomic.LoadUint32(&d.uid)), auth.KGID(atomic.LoadUint32(&d.gid)))
|
||||
}
|
||||
|
||||
func (d *dentry) readlink(ctx context.Context) (string, error) {
|
||||
return d.fs.vfsfs.VirtualFilesystem().ReadlinkAt(ctx, d.fs.creds, &vfs.PathOperation{
|
||||
Root: d.lowerVD,
|
||||
Start: d.lowerVD,
|
||||
})
|
||||
}
|
||||
|
||||
// FileDescription implements vfs.FileDescriptionImpl for verity fds.
|
||||
// FileDescription is a wrapper of the underlying lowerFD, with support to build
|
||||
// Merkle trees through the Linux fs-verity API to verify contents read from
|
||||
// lowerFD.
|
||||
type fileDescription struct {
|
||||
vfsfd vfs.FileDescription
|
||||
vfs.FileDescriptionDefaultImpl
|
||||
vfs.LockFD
|
||||
|
||||
// d is the corresponding dentry to the fileDescription.
|
||||
d *dentry
|
||||
|
||||
// isDir specifies whehter the fileDescription points to a directory.
|
||||
isDir bool
|
||||
|
||||
// lowerFD is the FileDescription corresponding to the file in the
|
||||
// underlying file system.
|
||||
lowerFD *vfs.FileDescription
|
||||
|
||||
// merkleReader is the read-only FileDescription corresponding to the
|
||||
// Merkle tree file in the underlying file system.
|
||||
merkleReader *vfs.FileDescription
|
||||
|
||||
// merkleWriter is the FileDescription corresponding to the Merkle tree
|
||||
// file in the underlying file system for writing. This should only be
|
||||
// used when allowRuntimeEnable is set to true.
|
||||
merkleWriter *vfs.FileDescription
|
||||
|
||||
// parentMerkleWriter is the FileDescription of the Merkle tree for the
|
||||
// directory that contains the current file/directory. This is only used
|
||||
// if allowRuntimeEnable is set to true.
|
||||
parentMerkleWriter *vfs.FileDescription
|
||||
}
|
||||
|
||||
// Release implements vfs.FileDescriptionImpl.Release.
|
||||
func (fd *fileDescription) Release(ctx context.Context) {
|
||||
fd.lowerFD.DecRef(ctx)
|
||||
fd.merkleReader.DecRef(ctx)
|
||||
if fd.merkleWriter != nil {
|
||||
fd.merkleWriter.DecRef(ctx)
|
||||
}
|
||||
if fd.parentMerkleWriter != nil {
|
||||
fd.parentMerkleWriter.DecRef(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// Stat implements vfs.FileDescriptionImpl.Stat.
|
||||
func (fd *fileDescription) Stat(ctx context.Context, opts vfs.StatOptions) (linux.Statx, error) {
|
||||
// TODO(b/162788573): Add integrity check for metadata.
|
||||
stat, err := fd.lowerFD.Stat(ctx, opts)
|
||||
if err != nil {
|
||||
return linux.Statx{}, err
|
||||
}
|
||||
return stat, nil
|
||||
}
|
||||
|
||||
// SetStat implements vfs.FileDescriptionImpl.SetStat.
|
||||
func (fd *fileDescription) SetStat(ctx context.Context, opts vfs.SetStatOptions) error {
|
||||
// Verity files are read-only.
|
||||
return syserror.EPERM
|
||||
}
|
||||
|
||||
// LockPOSIX implements vfs.FileDescriptionImpl.LockPOSIX.
|
||||
func (fd *fileDescription) LockPOSIX(ctx context.Context, uid fslock.UniqueID, t fslock.LockType, start, length uint64, whence int16, block fslock.Blocker) error {
|
||||
return fd.Locks().LockPOSIX(ctx, &fd.vfsfd, uid, t, start, length, whence, block)
|
||||
}
|
||||
|
||||
// UnlockPOSIX implements vfs.FileDescriptionImpl.UnlockPOSIX.
|
||||
func (fd *fileDescription) UnlockPOSIX(ctx context.Context, uid fslock.UniqueID, start, length uint64, whence int16) error {
|
||||
return fd.Locks().UnlockPOSIX(ctx, &fd.vfsfd, uid, start, length, whence)
|
||||
}
|
||||
Reference in New Issue
Block a user