Add support for device special files to VFS2 tmpfs.
PiperOrigin-RevId: 291471892
This commit is contained in:
parent
878bda6e19
commit
18a7e1309d
|
@ -20,6 +20,7 @@ go_library(
|
||||||
name = "tmpfs",
|
name = "tmpfs",
|
||||||
srcs = [
|
srcs = [
|
||||||
"dentry_list.go",
|
"dentry_list.go",
|
||||||
|
"device_file.go",
|
||||||
"directory.go",
|
"directory.go",
|
||||||
"filesystem.go",
|
"filesystem.go",
|
||||||
"named_pipe.go",
|
"named_pipe.go",
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
// 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 tmpfs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gvisor.dev/gvisor/pkg/abi/linux"
|
||||||
|
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
|
||||||
|
"gvisor.dev/gvisor/pkg/sentry/vfs"
|
||||||
|
)
|
||||||
|
|
||||||
|
type deviceFile struct {
|
||||||
|
inode inode
|
||||||
|
kind vfs.DeviceKind
|
||||||
|
major uint32
|
||||||
|
minor uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *filesystem) newDeviceFile(creds *auth.Credentials, mode linux.FileMode, kind vfs.DeviceKind, major, minor uint32) *inode {
|
||||||
|
file := &deviceFile{
|
||||||
|
kind: kind,
|
||||||
|
major: major,
|
||||||
|
minor: minor,
|
||||||
|
}
|
||||||
|
file.inode.init(file, fs, creds, mode)
|
||||||
|
file.inode.nlink = 1 // from parent directory
|
||||||
|
return &file.inode
|
||||||
|
}
|
|
@ -228,23 +228,26 @@ func (fs *filesystem) MkdirAt(ctx context.Context, rp *vfs.ResolvingPath, opts v
|
||||||
// MknodAt implements vfs.FilesystemImpl.MknodAt.
|
// MknodAt implements vfs.FilesystemImpl.MknodAt.
|
||||||
func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error {
|
func (fs *filesystem) MknodAt(ctx context.Context, rp *vfs.ResolvingPath, opts vfs.MknodOptions) error {
|
||||||
return fs.doCreateAt(rp, false /* dir */, func(parent *dentry, name string) error {
|
return fs.doCreateAt(rp, false /* dir */, func(parent *dentry, name string) error {
|
||||||
|
var childInode *inode
|
||||||
switch opts.Mode.FileType() {
|
switch opts.Mode.FileType() {
|
||||||
case 0, linux.S_IFREG:
|
case 0, linux.S_IFREG:
|
||||||
child := fs.newDentry(fs.newRegularFile(rp.Credentials(), opts.Mode))
|
childInode = fs.newRegularFile(rp.Credentials(), opts.Mode)
|
||||||
parent.vfsd.InsertChild(&child.vfsd, name)
|
|
||||||
parent.inode.impl.(*directory).childList.PushBack(child)
|
|
||||||
return nil
|
|
||||||
case linux.S_IFIFO:
|
case linux.S_IFIFO:
|
||||||
child := fs.newDentry(fs.newNamedPipe(rp.Credentials(), opts.Mode))
|
childInode = fs.newNamedPipe(rp.Credentials(), opts.Mode)
|
||||||
parent.vfsd.InsertChild(&child.vfsd, name)
|
case linux.S_IFBLK:
|
||||||
parent.inode.impl.(*directory).childList.PushBack(child)
|
childInode = fs.newDeviceFile(rp.Credentials(), opts.Mode, vfs.BlockDevice, opts.DevMajor, opts.DevMinor)
|
||||||
return nil
|
case linux.S_IFCHR:
|
||||||
case linux.S_IFBLK, linux.S_IFCHR, linux.S_IFSOCK:
|
childInode = fs.newDeviceFile(rp.Credentials(), opts.Mode, vfs.CharDevice, opts.DevMajor, opts.DevMinor)
|
||||||
|
case linux.S_IFSOCK:
|
||||||
// Not yet supported.
|
// Not yet supported.
|
||||||
return syserror.EPERM
|
return syserror.EPERM
|
||||||
default:
|
default:
|
||||||
return syserror.EINVAL
|
return syserror.EINVAL
|
||||||
}
|
}
|
||||||
|
child := fs.newDentry(childInode)
|
||||||
|
parent.vfsd.InsertChild(&child.vfsd, name)
|
||||||
|
parent.inode.impl.(*directory).childList.PushBack(child)
|
||||||
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,7 +267,7 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return d.open(ctx, rp, opts.Flags, false /* afterCreate */)
|
return d.open(ctx, rp, &opts, false /* afterCreate */)
|
||||||
}
|
}
|
||||||
|
|
||||||
mustCreate := opts.Flags&linux.O_EXCL != 0
|
mustCreate := opts.Flags&linux.O_EXCL != 0
|
||||||
|
@ -279,7 +282,7 @@ func (fs *filesystem) OpenAt(ctx context.Context, rp *vfs.ResolvingPath, opts vf
|
||||||
if mustCreate {
|
if mustCreate {
|
||||||
return nil, syserror.EEXIST
|
return nil, syserror.EEXIST
|
||||||
}
|
}
|
||||||
return start.open(ctx, rp, opts.Flags, false /* afterCreate */)
|
return start.open(ctx, rp, &opts, false /* afterCreate */)
|
||||||
}
|
}
|
||||||
afterTrailingSymlink:
|
afterTrailingSymlink:
|
||||||
parent, err := walkParentDirLocked(rp, start)
|
parent, err := walkParentDirLocked(rp, start)
|
||||||
|
@ -313,7 +316,7 @@ afterTrailingSymlink:
|
||||||
child := fs.newDentry(fs.newRegularFile(rp.Credentials(), opts.Mode))
|
child := fs.newDentry(fs.newRegularFile(rp.Credentials(), opts.Mode))
|
||||||
parent.vfsd.InsertChild(&child.vfsd, name)
|
parent.vfsd.InsertChild(&child.vfsd, name)
|
||||||
parent.inode.impl.(*directory).childList.PushBack(child)
|
parent.inode.impl.(*directory).childList.PushBack(child)
|
||||||
return child.open(ctx, rp, opts.Flags, true)
|
return child.open(ctx, rp, &opts, true)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -327,11 +330,11 @@ afterTrailingSymlink:
|
||||||
if mustCreate {
|
if mustCreate {
|
||||||
return nil, syserror.EEXIST
|
return nil, syserror.EEXIST
|
||||||
}
|
}
|
||||||
return child.open(ctx, rp, opts.Flags, false)
|
return child.open(ctx, rp, &opts, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, flags uint32, afterCreate bool) (*vfs.FileDescription, error) {
|
func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, opts *vfs.OpenOptions, afterCreate bool) (*vfs.FileDescription, error) {
|
||||||
ats := vfs.AccessTypesForOpenFlags(flags)
|
ats := vfs.AccessTypesForOpenFlags(opts.Flags)
|
||||||
if !afterCreate {
|
if !afterCreate {
|
||||||
if err := d.inode.checkPermissions(rp.Credentials(), ats, d.inode.isDir()); err != nil {
|
if err := d.inode.checkPermissions(rp.Credentials(), ats, d.inode.isDir()); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -340,10 +343,10 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, flags uint32,
|
||||||
switch impl := d.inode.impl.(type) {
|
switch impl := d.inode.impl.(type) {
|
||||||
case *regularFile:
|
case *regularFile:
|
||||||
var fd regularFileFD
|
var fd regularFileFD
|
||||||
if err := fd.vfsfd.Init(&fd, flags, rp.Mount(), &d.vfsd, &vfs.FileDescriptionOptions{}); err != nil {
|
if err := fd.vfsfd.Init(&fd, opts.Flags, rp.Mount(), &d.vfsd, &vfs.FileDescriptionOptions{}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if flags&linux.O_TRUNC != 0 {
|
if opts.Flags&linux.O_TRUNC != 0 {
|
||||||
impl.mu.Lock()
|
impl.mu.Lock()
|
||||||
impl.data.Truncate(0, impl.memFile)
|
impl.data.Truncate(0, impl.memFile)
|
||||||
atomic.StoreUint64(&impl.size, 0)
|
atomic.StoreUint64(&impl.size, 0)
|
||||||
|
@ -356,7 +359,7 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, flags uint32,
|
||||||
return nil, syserror.EISDIR
|
return nil, syserror.EISDIR
|
||||||
}
|
}
|
||||||
var fd directoryFD
|
var fd directoryFD
|
||||||
if err := fd.vfsfd.Init(&fd, flags, rp.Mount(), &d.vfsd, &vfs.FileDescriptionOptions{}); err != nil {
|
if err := fd.vfsfd.Init(&fd, opts.Flags, rp.Mount(), &d.vfsd, &vfs.FileDescriptionOptions{}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &fd.vfsfd, nil
|
return &fd.vfsfd, nil
|
||||||
|
@ -364,7 +367,9 @@ func (d *dentry) open(ctx context.Context, rp *vfs.ResolvingPath, flags uint32,
|
||||||
// Can't open symlinks without O_PATH (which is unimplemented).
|
// Can't open symlinks without O_PATH (which is unimplemented).
|
||||||
return nil, syserror.ELOOP
|
return nil, syserror.ELOOP
|
||||||
case *namedPipe:
|
case *namedPipe:
|
||||||
return newNamedPipeFD(ctx, impl, rp, &d.vfsd, flags)
|
return newNamedPipeFD(ctx, impl, rp, &d.vfsd, opts.Flags)
|
||||||
|
case *deviceFile:
|
||||||
|
return rp.VirtualFilesystem().OpenDeviceSpecialFile(ctx, rp.Mount(), &d.vfsd, impl.kind, impl.major, impl.minor, opts)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown inode type: %T", d.inode.impl))
|
panic(fmt.Sprintf("unknown inode type: %T", d.inode.impl))
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,6 +149,10 @@ type inode struct {
|
||||||
ctime int64 // nanoseconds
|
ctime int64 // nanoseconds
|
||||||
mtime int64 // nanoseconds
|
mtime int64 // nanoseconds
|
||||||
|
|
||||||
|
// Only meaningful for device special files.
|
||||||
|
rdevMajor uint32
|
||||||
|
rdevMinor uint32
|
||||||
|
|
||||||
impl interface{} // immutable
|
impl interface{} // immutable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,6 +273,15 @@ func (i *inode) statTo(stat *linux.Statx) {
|
||||||
stat.Blocks = allocatedBlocksForSize(stat.Size)
|
stat.Blocks = allocatedBlocksForSize(stat.Size)
|
||||||
case *namedPipe:
|
case *namedPipe:
|
||||||
stat.Mode |= linux.S_IFIFO
|
stat.Mode |= linux.S_IFIFO
|
||||||
|
case *deviceFile:
|
||||||
|
switch impl.kind {
|
||||||
|
case vfs.BlockDevice:
|
||||||
|
stat.Mode |= linux.S_IFBLK
|
||||||
|
case vfs.CharDevice:
|
||||||
|
stat.Mode |= linux.S_IFCHR
|
||||||
|
}
|
||||||
|
stat.RdevMajor = impl.major
|
||||||
|
stat.RdevMinor = impl.minor
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown inode type: %T", i.impl))
|
panic(fmt.Sprintf("unknown inode type: %T", i.impl))
|
||||||
}
|
}
|
||||||
|
@ -309,12 +322,8 @@ func (i *inode) setStat(stat linux.Statx) error {
|
||||||
}
|
}
|
||||||
case *directory:
|
case *directory:
|
||||||
return syserror.EISDIR
|
return syserror.EISDIR
|
||||||
case *symlink:
|
|
||||||
return syserror.EINVAL
|
|
||||||
case *namedPipe:
|
|
||||||
// Nothing.
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown inode type: %T", i.impl))
|
return syserror.EINVAL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mask&linux.STATX_ATIME != 0 {
|
if mask&linux.STATX_ATIME != 0 {
|
||||||
|
@ -353,13 +362,22 @@ func allocatedBlocksForSize(size uint64) uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *inode) direntType() uint8 {
|
func (i *inode) direntType() uint8 {
|
||||||
switch i.impl.(type) {
|
switch impl := i.impl.(type) {
|
||||||
case *regularFile:
|
case *regularFile:
|
||||||
return linux.DT_REG
|
return linux.DT_REG
|
||||||
case *directory:
|
case *directory:
|
||||||
return linux.DT_DIR
|
return linux.DT_DIR
|
||||||
case *symlink:
|
case *symlink:
|
||||||
return linux.DT_LNK
|
return linux.DT_LNK
|
||||||
|
case *deviceFile:
|
||||||
|
switch impl.kind {
|
||||||
|
case vfs.BlockDevice:
|
||||||
|
return linux.DT_BLK
|
||||||
|
case vfs.CharDevice:
|
||||||
|
return linux.DT_CHR
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("unknown vfs.DeviceKind: %v", impl.kind))
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("unknown inode type: %T", i.impl))
|
panic(fmt.Sprintf("unknown inode type: %T", i.impl))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue