155 lines
4.0 KiB
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 := ®ularFile{}
|
|
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
|
|
}
|