gvisor/pkg/sentry/fsimpl/memfs/regular_file.go

155 lines
4.0 KiB
Go

// Copyright 2019 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 memfs
import (
"io"
"sync"
"sync/atomic"
"gvisor.dev/gvisor/pkg/abi/linux"
"gvisor.dev/gvisor/pkg/sentry/context"
"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
"gvisor.dev/gvisor/pkg/sentry/usermem"
"gvisor.dev/gvisor/pkg/sentry/vfs"
"gvisor.dev/gvisor/pkg/syserror"
)
type regularFile struct {
inode inode
mu sync.RWMutex
data []byte
// dataLen is len(data), but accessed using atomic memory operations to
// avoid locking in inode.stat().
dataLen int64
}
func (fs *filesystem) newRegularFile(creds *auth.Credentials, mode linux.FileMode) *inode {
file := &regularFile{}
file.inode.init(file, fs, creds, mode)
file.inode.nlink = 1 // from parent directory
return &file.inode
}
type regularFileFD struct {
fileDescription
// These are immutable.
readable bool
writable bool
// off is the file offset. off is accessed using atomic memory operations.
// offMu serializes operations that may mutate off.
off int64
offMu sync.Mutex
}
// Release implements vfs.FileDescriptionImpl.Release.
func (fd *regularFileFD) Release() {
if fd.writable {
fd.vfsfd.VirtualDentry().Mount().EndWrite()
}
}
// PRead implements vfs.FileDescriptionImpl.PRead.
func (fd *regularFileFD) PRead(ctx context.Context, dst usermem.IOSequence, offset int64, opts vfs.ReadOptions) (int64, error) {
if !fd.readable {
return 0, syserror.EINVAL
}
f := fd.inode().impl.(*regularFile)
f.mu.RLock()
if offset >= int64(len(f.data)) {
f.mu.RUnlock()
return 0, io.EOF
}
n, err := dst.CopyOut(ctx, f.data[offset:])
f.mu.RUnlock()
return int64(n), err
}
// Read implements vfs.FileDescriptionImpl.Read.
func (fd *regularFileFD) Read(ctx context.Context, dst usermem.IOSequence, opts vfs.ReadOptions) (int64, error) {
fd.offMu.Lock()
n, err := fd.PRead(ctx, dst, fd.off, opts)
fd.off += n
fd.offMu.Unlock()
return n, err
}
// PWrite implements vfs.FileDescriptionImpl.PWrite.
func (fd *regularFileFD) PWrite(ctx context.Context, src usermem.IOSequence, offset int64, opts vfs.WriteOptions) (int64, error) {
if !fd.writable {
return 0, syserror.EINVAL
}
if offset < 0 {
return 0, syserror.EINVAL
}
srclen := src.NumBytes()
if srclen == 0 {
return 0, nil
}
f := fd.inode().impl.(*regularFile)
f.mu.Lock()
end := offset + srclen
if end < offset {
// Overflow.
f.mu.Unlock()
return 0, syserror.EFBIG
}
if end > f.dataLen {
f.data = append(f.data, make([]byte, end-f.dataLen)...)
atomic.StoreInt64(&f.dataLen, end)
}
n, err := src.CopyIn(ctx, f.data[offset:end])
f.mu.Unlock()
return int64(n), err
}
// Write implements vfs.FileDescriptionImpl.Write.
func (fd *regularFileFD) Write(ctx context.Context, src usermem.IOSequence, opts vfs.WriteOptions) (int64, error) {
fd.offMu.Lock()
n, err := fd.PWrite(ctx, src, fd.off, opts)
fd.off += n
fd.offMu.Unlock()
return n, err
}
// Seek implements vfs.FileDescriptionImpl.Seek.
func (fd *regularFileFD) Seek(ctx context.Context, offset int64, whence int32) (int64, error) {
fd.offMu.Lock()
defer fd.offMu.Unlock()
switch whence {
case linux.SEEK_SET:
// use offset as specified
case linux.SEEK_CUR:
offset += fd.off
case linux.SEEK_END:
offset += atomic.LoadInt64(&fd.inode().impl.(*regularFile).dataLen)
default:
return 0, syserror.EINVAL
}
if offset < 0 {
return 0, syserror.EINVAL
}
fd.off = offset
return offset, nil
}
// Sync implements vfs.FileDescriptionImpl.Sync.
func (fd *regularFileFD) Sync(ctx context.Context) error {
return nil
}