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/erofs/erofs.go
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,11 @@ func (d *dentry) Watches() *vfs.Watches {
// OnZeroWatches implements vfs.DentryImpl.OnZeroWatches.
func (d *dentry) OnZeroWatches(ctx context.Context) {}

// IsDir implements vfs.DentryImpl.IsDir.
func (d *dentry) IsDir() bool {
return d.inode.IsDir()
}

func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions) (*vfs.FileDescription, error) {
ats := vfs.AccessTypesForOpenFlags(opts)
if err := d.inode.checkPermissions(rp.Credentials(), ats); err != nil {
Expand Down
5 changes: 5 additions & 0 deletions pkg/sentry/fsimpl/gofer/gofer.go
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,11 @@ func (d *dentry) OnZeroWatches(ctx context.Context) {
d.checkCachingLocked(ctx, false /* renameMuWriteLocked */)
}

// IsDir implements vfs.DentryImpl.IsDir.
func (d *dentry) IsDir() bool {
return d.isDir()
}

// checkCachingLocked should be called after d's reference count becomes 0 or
// it becomes disowned.
//
Expand Down
5 changes: 5 additions & 0 deletions pkg/sentry/fsimpl/kernfs/kernfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,11 @@ func (d *Dentry) Watches() *vfs.Watches {
// OnZeroWatches implements vfs.Dentry.OnZeroWatches.
func (d *Dentry) OnZeroWatches(context.Context) {}

// IsDir implements vfs.DentryImpl.IsDir.
func (d *Dentry) IsDir() bool {
return d.isDir()
}

// insertChild inserts child into the vfs dentry cache with the given name under
// this dentry. This does not update the directory inode, so calling this on its
// own isn't sufficient to insert a child into a directory.
Expand Down
5 changes: 5 additions & 0 deletions pkg/sentry/fsimpl/overlay/overlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,11 @@ func (d *dentry) OnZeroWatches(ctx context.Context) {
}
}

// IsDir implements vfs.DentryImpl.IsDir.
func (d *dentry) IsDir() bool {
return d.isDir()
}

// iterLayers invokes yield on each layer comprising d, from top to bottom. If
// any call to yield returns false, iterLayer stops iteration.
func (d *dentry) iterLayers(yield func(vd vfs.VirtualDentry, isUpper bool) bool) {
Expand Down
5 changes: 5 additions & 0 deletions pkg/sentry/fsimpl/tmpfs/tmpfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,11 @@ func (d *dentry) Watches() *vfs.Watches {
// OnZeroWatches implements vfs.Dentry.OnZeroWatches.
func (d *dentry) OnZeroWatches(context.Context) {}

// IsDir implements vfs.DentryImpl.IsDir.
func (d *dentry) IsDir() bool {
return d.inode.isDir()
}

// inode represents a filesystem object.
//
// +stateify savable
Expand Down
5 changes: 5 additions & 0 deletions pkg/sentry/vfs/anonfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,8 @@ func (d *anonDentry) Watches() *Watches {

// OnZeroWatches implements Dentry.OnZeroWatches.
func (d *anonDentry) OnZeroWatches(context.Context) {}

// IsDir implements DentryImpl.IsDir.
func (d *anonDentry) IsDir() bool {
return false
}
3 changes: 3 additions & 0 deletions pkg/sentry/vfs/dentry.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ type DentryImpl interface {
// may acquire inotify locks, so to prevent deadlock, no inotify locks should
// be held by the caller.
OnZeroWatches(ctx context.Context)

// IsDir returns true if the file represented by this dentry is a directory.
IsDir() bool
Comment on lines +141 to +143
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to extend DentryImpl. The information about whether an Dentry is a Dir or not is available via vfsObj.StatAt. You can find an example here:

stat, err := vfs.StatAt(ctx, creds, &PathOperation{
Root: vd,
Start: vd,
}, &StatOptions{
Mask: linux.STATX_MODE,
})
if err != nil {
return nil, err
}
if stat.Mode&linux.S_IFDIR == 0 {
return nil, linuxerr.ENOTDIR
}

In vfs.BindAt, we already have the VDs from GetDentryAt, so should be easy to call vfs.StatAt() with that directly, as shown above.

}

// IncRef increments d's reference count.
Expand Down
6 changes: 6 additions & 0 deletions pkg/sentry/vfs/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,12 @@ func (vfs *VirtualFilesystem) BindAt(ctx context.Context, creds *auth.Credential
return err
}

// Linux's graft_tree() (fs/namespace.c) returns ENOTDIR if the source and
// target have mismatched types (one is a directory, the other is not).
if sourceVd.Dentry().Impl().IsDir() != targetVd.Dentry().Impl().IsDir() {
return linuxerr.ENOTDIR
}

vfs.lockMounts()
defer vfs.unlockMounts(ctx)
mp, err := vfs.lockMountpoint(targetVd)
Expand Down
32 changes: 32 additions & 0 deletions test/syscalls/linux/mount.cc
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,38 @@ TEST(MountTest, BindMountReadonly) {
EXPECT_EQ(s.st_size, strlen(msg));
}

// Test that bind mounting a directory onto a regular file fails with ENOTDIR.
TEST(MountTest, BindMountDirectoryOntoFileFails) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));

auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
auto const source_dir =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path()));
auto const target_file =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path()));

// Bind mounting a directory onto a file should fail with ENOTDIR.
EXPECT_THAT(mount(source_dir.path().c_str(), target_file.path().c_str(),
nullptr, MS_BIND, nullptr),
SyscallFailsWithErrno(ENOTDIR));
}

// Test that bind mounting a regular file onto a directory fails with ENOTDIR.
TEST(MountTest, BindMountFileOntoDirectoryFails) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));

auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
auto const source_file =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path()));
auto const target_dir =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(dir.path()));

// Bind mounting a file onto a directory should fail with ENOTDIR.
EXPECT_THAT(mount(source_file.path().c_str(), target_dir.path().c_str(),
nullptr, MS_BIND, nullptr),
SyscallFailsWithErrno(ENOTDIR));
}

PosixErrorOr<absl::Time> ATime(absl::string_view file) {
struct stat s = {};
if (stat(std::string(file).c_str(), &s) == -1) {
Expand Down