Add basic plumbing for splice and stub implementation.

This does not actually implement an efficient splice or sendfile. Rather, it
adds a generic plumbing to the file internals so that this can be added. All
file implementations use the stub fileutil.NoSplice implementation, which
causes sendfile and splice to fall back to an internal copy.

A basic splice system call interface is added, along with a test.

PiperOrigin-RevId: 249335960
Change-Id: Ic5568be2af0a505c19e7aec66d5af2480ab0939b
This commit is contained in:
Adin Scannell
2019-05-21 15:18:12 -07:00
committed by Shentubot
parent adeb99709b
commit 9cdae51fec
50 changed files with 1209 additions and 230 deletions
+1
View File
@@ -45,6 +45,7 @@ go_library(
"shm.go",
"signal.go",
"socket.go",
"splice.go",
"tcp.go",
"time.go",
"timer.go",
@@ -1,4 +1,4 @@
// Copyright 2018 The gVisor Authors.
// 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.
@@ -12,13 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
package fs
package linux
import "io"
var (
_ = io.Reader(&FileReader{})
_ = io.ReaderAt(&FileReader{})
_ = io.Writer(&FileWriter{})
_ = io.WriterAt(&FileWriter{})
// Constants for splice(2), sendfile(2) and tee(2).
const (
SPLICE_F_MOVE = 1 << iota
SPLICE_F_NONBLOCK
SPLICE_F_MORE
SPLICE_F_GIFT
)
+2 -2
View File
@@ -40,6 +40,7 @@ go_library(
"restore.go",
"save.go",
"seek.go",
"splice.go",
"sync.go",
],
importpath = "gvisor.googlesource.com/gvisor/pkg/sentry/fs",
@@ -51,6 +52,7 @@ go_library(
"//pkg/metric",
"//pkg/p9",
"//pkg/refs",
"//pkg/secio",
"//pkg/sentry/arch",
"//pkg/sentry/context",
"//pkg/sentry/device",
@@ -66,7 +68,6 @@ go_library(
"//pkg/sentry/usermem",
"//pkg/state",
"//pkg/syserror",
"//pkg/tcpip",
"//pkg/waiter",
],
)
@@ -122,7 +123,6 @@ go_test(
srcs = [
"dirent_cache_test.go",
"dirent_refs_test.go",
"file_test.go",
"mount_test.go",
"path_test.go",
],
+2 -1
View File
@@ -42,11 +42,12 @@ const (
//
// +stateify savable
type Area struct {
waiter.AlwaysReady `state:"nosave"`
fsutil.FileNoFsync `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
ad *Device
+2 -1
View File
@@ -86,10 +86,11 @@ func (bd *Device) GetFile(ctx context.Context, d *fs.Dirent, flags fs.FileFlags)
//
// +stateify savable
type Proc struct {
waiter.AlwaysReady `state:"nosave"`
fsutil.FileNoFsync `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
bd *Device
task *kernel.Task
+2 -1
View File
@@ -60,6 +60,7 @@ func (f *fullDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.Fi
// +stateify savable
type fullFileOperations struct {
waiter.AlwaysReady `state:"nosave"`
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
@@ -68,8 +69,8 @@ type fullFileOperations struct {
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
readZeros `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
}
var _ fs.FileOperations = (*fullFileOperations)(nil)
+3 -1
View File
@@ -64,6 +64,7 @@ type nullFileOperations struct {
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRead `state:"nosave"`
@@ -104,14 +105,15 @@ func (zd *zeroDevice) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.F
type zeroFileOperations struct {
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNoopWrite `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
readZeros `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
readZeros `state:"nosave"`
}
var _ fs.FileOperations = (*zeroFileOperations)(nil)
+1
View File
@@ -61,6 +61,7 @@ type randomFileOperations struct {
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
+1
View File
@@ -43,6 +43,7 @@ type pipeOperations struct {
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.Queue `state:"nosave"`
+91 -50
View File
@@ -21,7 +21,6 @@ import (
"time"
"gvisor.googlesource.com/gvisor/pkg/amutex"
"gvisor.googlesource.com/gvisor/pkg/log"
"gvisor.googlesource.com/gvisor/pkg/metric"
"gvisor.googlesource.com/gvisor/pkg/refs"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
@@ -35,8 +34,13 @@ import (
)
var (
// RecordWaitTime controls writing metrics for filesystem reads. Enabling this comes at a small
// CPU cost due to performing two monotonic clock reads per read call.
// RecordWaitTime controls writing metrics for filesystem reads.
// Enabling this comes at a small CPU cost due to performing two
// monotonic clock reads per read call.
//
// Note that this is only performed in the direct read path, and may
// not be consistently applied for other forms of reads, such as
// splice.
RecordWaitTime = false
reads = metric.MustCreateNewUint64Metric("/fs/reads", false /* sync */, "Number of file reads.")
@@ -306,14 +310,28 @@ func (f *File) Writev(ctx context.Context, src usermem.IOSequence) (int64, error
return 0, syserror.ErrInterrupted
}
offset, err := f.checkWriteLocked(ctx, &src, f.offset)
if err != nil {
f.mu.Unlock()
return 0, err
// Handle append mode.
if f.Flags().Append {
if err := f.offsetForAppend(ctx, &f.offset); err != nil {
f.mu.Unlock()
return 0, err
}
}
n, err := f.FileOperations.Write(ctx, f, src, offset)
// Enforce file limits.
limit, ok := f.checkLimit(ctx, f.offset)
switch {
case ok && limit == 0:
f.mu.Unlock()
return 0, syserror.ErrExceedsFileSizeLimit
case ok:
src = src.TakeFirst64(limit)
}
// We must hold the lock during the write.
n, err := f.FileOperations.Write(ctx, f, src, f.offset)
if n >= 0 {
atomic.StoreInt64(&f.offset, offset+n)
atomic.StoreInt64(&f.offset, f.offset+n)
}
f.mu.Unlock()
return n, err
@@ -325,51 +343,67 @@ func (f *File) Writev(ctx context.Context, src usermem.IOSequence) (int64, error
//
// Otherwise same as Writev.
func (f *File) Pwritev(ctx context.Context, src usermem.IOSequence, offset int64) (int64, error) {
if !f.mu.Lock(ctx) {
return 0, syserror.ErrInterrupted
// "POSIX requires that opening a file with the O_APPEND flag should
// have no effect on the location at which pwrite() writes data.
// However, on Linux, if a file is opened with O_APPEND, pwrite()
// appends data to the end of the file, regardless of the value of
// offset."
if f.Flags().Append {
if !f.mu.Lock(ctx) {
return 0, syserror.ErrInterrupted
}
defer f.mu.Unlock()
if err := f.offsetForAppend(ctx, &offset); err != nil {
f.mu.Unlock()
return 0, err
}
}
offset, err := f.checkWriteLocked(ctx, &src, offset)
if err != nil {
f.mu.Unlock()
return 0, err
// Enforce file limits.
limit, ok := f.checkLimit(ctx, offset)
switch {
case ok && limit == 0:
return 0, syserror.ErrExceedsFileSizeLimit
case ok:
src = src.TakeFirst64(limit)
}
n, err := f.FileOperations.Write(ctx, f, src, offset)
f.mu.Unlock()
return n, err
return f.FileOperations.Write(ctx, f, src, offset)
}
// checkWriteLocked returns the offset to write at or an error if the write
// would not succeed. May update src to fit a write operation into a file
// size limit.
func (f *File) checkWriteLocked(ctx context.Context, src *usermem.IOSequence, offset int64) (int64, error) {
// Handle append only files. Note that this is still racy for network
// filesystems.
if f.Flags().Append {
uattr, err := f.Dirent.Inode.UnstableAttr(ctx)
if err != nil {
// This is an odd error, most likely it is evidence
// that something is terribly wrong with the filesystem.
// Return a generic EIO error.
log.Warningf("Failed to check write of inode %#v: %v", f.Dirent.Inode.StableAttr, err)
return offset, syserror.EIO
}
offset = uattr.Size
// offsetForAppend sets the given offset to the end of the file.
//
// Precondition: the underlying file mutex should be held.
func (f *File) offsetForAppend(ctx context.Context, offset *int64) error {
uattr, err := f.Dirent.Inode.UnstableAttr(ctx)
if err != nil {
// This is an odd error, we treat it as evidence that
// something is terribly wrong with the filesystem.
return syserror.EIO
}
// Is this a regular file?
// Update the offset.
*offset = uattr.Size
return nil
}
// checkLimit checks the offset that the write will be performed at. The
// returned boolean indicates that the write must be limited. The returned
// integer indicates the new maximum write length.
func (f *File) checkLimit(ctx context.Context, offset int64) (int64, bool) {
if IsRegular(f.Dirent.Inode.StableAttr) {
// Enforce size limits.
fileSizeLimit := limits.FromContext(ctx).Get(limits.FileSize).Cur
if fileSizeLimit <= math.MaxInt64 {
if offset >= int64(fileSizeLimit) {
return offset, syserror.ErrExceedsFileSizeLimit
return 0, true
}
*src = src.TakeFirst64(int64(fileSizeLimit) - offset)
return int64(fileSizeLimit) - offset, true
}
}
return offset, nil
return 0, false
}
// Fsync calls f.FileOperations.Fsync with f as the File.
@@ -466,8 +500,13 @@ func (f *File) Async(newAsync func() FileAsync) FileAsync {
return f.async
}
// FileReader implements io.Reader and io.ReaderAt.
type FileReader struct {
// lockedReader implements io.Reader and io.ReaderAt.
//
// Note this reads the underlying file using the file operations directly. It
// is the responsibility of the caller to ensure that locks are appropriately
// held and offsets updated if required. This should be used only by internal
// functions that perform these operations and checks at other times.
type lockedReader struct {
// Ctx is the context for the file reader.
Ctx context.Context
@@ -476,19 +515,21 @@ type FileReader struct {
}
// Read implements io.Reader.Read.
func (r *FileReader) Read(buf []byte) (int, error) {
n, err := r.File.Readv(r.Ctx, usermem.BytesIOSequence(buf))
func (r *lockedReader) Read(buf []byte) (int, error) {
n, err := r.File.FileOperations.Read(r.Ctx, r.File, usermem.BytesIOSequence(buf), r.File.offset)
return int(n), err
}
// ReadAt implements io.Reader.ReadAt.
func (r *FileReader) ReadAt(buf []byte, offset int64) (int, error) {
n, err := r.File.Preadv(r.Ctx, usermem.BytesIOSequence(buf), offset)
func (r *lockedReader) ReadAt(buf []byte, offset int64) (int, error) {
n, err := r.File.FileOperations.Read(r.Ctx, r.File, usermem.BytesIOSequence(buf), offset)
return int(n), err
}
// FileWriter implements io.Writer and io.WriterAt.
type FileWriter struct {
// lockedWriter implements io.Writer and io.WriterAt.
//
// The same constraints as lockedReader apply; see above.
type lockedWriter struct {
// Ctx is the context for the file writer.
Ctx context.Context
@@ -497,13 +538,13 @@ type FileWriter struct {
}
// Write implements io.Writer.Write.
func (w *FileWriter) Write(buf []byte) (int, error) {
n, err := w.File.Writev(w.Ctx, usermem.BytesIOSequence(buf))
func (w *lockedWriter) Write(buf []byte) (int, error) {
n, err := w.File.FileOperations.Write(w.Ctx, w.File, usermem.BytesIOSequence(buf), w.File.offset)
return int(n), err
}
// WriteAt implements io.Writer.WriteAt.
func (w *FileWriter) WriteAt(buf []byte, offset int64) (int, error) {
n, err := w.File.Pwritev(w.Ctx, usermem.BytesIOSequence(buf), offset)
func (w *lockedWriter) WriteAt(buf []byte, offset int64) (int, error) {
n, err := w.File.FileOperations.Write(w.Ctx, w.File, usermem.BytesIOSequence(buf), offset)
return int(n), err
}
+47
View File
@@ -22,6 +22,38 @@ import (
"gvisor.googlesource.com/gvisor/pkg/waiter"
)
// SpliceOpts define how a splice works.
type SpliceOpts struct {
// Length is the length of the splice operation.
Length int64
// SrcOffset indicates whether the existing source file offset should
// be used. If this is true, then the Start value below is used.
//
// When passed to FileOperations object, this should always be true as
// the offset will be provided by a layer above, unless the object in
// question is a pipe or socket. This value can be relied upon for such
// an indicator.
SrcOffset bool
// SrcStart is the start of the source file. This is used only if
// SrcOffset is false.
SrcStart int64
// Dup indicates that the contents should not be consumed from the
// source (e.g. in the case of a socket or a pipe), but duplicated.
Dup bool
// DstOffset indicates that the destination file offset should be used.
//
// See SrcOffset for additional information.
DstOffset bool
// DstStart is the start of the destination file. This is used only if
// DstOffset is false.
DstStart int64
}
// FileOperations are operations on a File that diverge per file system.
//
// Operations that take a *File may use only the following interfaces:
@@ -67,6 +99,15 @@ type FileOperations interface {
// Read must not be called if !FileFlags.Read.
Read(ctx context.Context, file *File, dst usermem.IOSequence, offset int64) (int64, error)
// WriteTo is a variant of read that takes another file as a
// destination. For a splice (copy or move from one file to another),
// first a WriteTo on the source is attempted, followed by a ReadFrom
// on the destination, following by a buffered copy with standard Read
// and Write operations.
//
// The same preconditions as Read apply.
WriteTo(ctx context.Context, file *File, dst *File, opts SpliceOpts) (int64, error)
// Write writes src to file at offset and returns the number of bytes
// written which must be greater than or equal to 0. Like Read, file
// systems that do not support writing at an offset (i.e. pipefs, sockfs)
@@ -81,6 +122,12 @@ type FileOperations interface {
// Write must not be called if !FileFlags.Write.
Write(ctx context.Context, file *File, src usermem.IOSequence, offset int64) (int64, error)
// ReadFrom is a variant of write that takes a another file as a
// source. See WriteTo for details regarding how this is called.
//
// The same preconditions as Write apply; FileFlags.Write must be set.
ReadFrom(ctx context.Context, file *File, src *File, opts SpliceOpts) (int64, error)
// Fsync writes buffered modifications of file and/or flushes in-flight
// operations to backing storage based on syncType. The range to sync is
// [start, end]. The end is inclusive so that the last byte of a maximally
+53 -30
View File
@@ -17,7 +17,6 @@ package fs
import (
"sync"
"gvisor.googlesource.com/gvisor/pkg/log"
"gvisor.googlesource.com/gvisor/pkg/refs"
"gvisor.googlesource.com/gvisor/pkg/sentry/arch"
"gvisor.googlesource.com/gvisor/pkg/sentry/context"
@@ -222,31 +221,50 @@ func (f *overlayFileOperations) IterateDir(ctx context.Context, dirCtx *DirCtx,
return offset + n, err
}
// Read implements FileOperations.Read.
func (f *overlayFileOperations) Read(ctx context.Context, file *File, dst usermem.IOSequence, offset int64) (int64, error) {
o := file.Dirent.Inode.overlay
// onTop performs the given operation on the top-most available layer.
func (f *overlayFileOperations) onTop(ctx context.Context, file *File, fn func(*File, FileOperations) error) error {
file.Dirent.Inode.overlay.copyMu.RLock()
defer file.Dirent.Inode.overlay.copyMu.RUnlock()
o.copyMu.RLock()
defer o.copyMu.RUnlock()
if o.upper != nil {
// We may need to acquire an open file handle to read from if
// copy up has occurred. Otherwise we risk reading from the
// wrong source.
f.upperMu.Lock()
if f.upper == nil {
var err error
f.upper, err = overlayFile(ctx, o.upper, file.Flags())
if err != nil {
f.upperMu.Unlock()
log.Warningf("failed to acquire handle with flags %v: %v", file.Flags(), err)
return 0, syserror.EIO
}
}
f.upperMu.Unlock()
return f.upper.FileOperations.Read(ctx, f.upper, dst, offset)
// Only lower layer is available.
if file.Dirent.Inode.overlay.upper == nil {
return fn(f.lower, f.lower.FileOperations)
}
return f.lower.FileOperations.Read(ctx, f.lower, dst, offset)
f.upperMu.Lock()
if f.upper == nil {
upper, err := overlayFile(ctx, file.Dirent.Inode.overlay.upper, file.Flags())
if err != nil {
// Something very wrong; return a generic filesystem
// error to avoid propagating internals.
f.upperMu.Unlock()
return syserror.EIO
}
// Save upper file.
f.upper = upper
}
f.upperMu.Unlock()
return fn(f.upper, f.upper.FileOperations)
}
// Read implements FileOperations.Read.
func (f *overlayFileOperations) Read(ctx context.Context, file *File, dst usermem.IOSequence, offset int64) (n int64, err error) {
err = f.onTop(ctx, file, func(file *File, ops FileOperations) error {
n, err = ops.Read(ctx, file, dst, offset)
return err // Will overwrite itself.
})
return
}
// WriteTo implements FileOperations.WriteTo.
func (f *overlayFileOperations) WriteTo(ctx context.Context, file *File, dst *File, opts SpliceOpts) (n int64, err error) {
err = f.onTop(ctx, file, func(file *File, ops FileOperations) error {
n, err = ops.WriteTo(ctx, file, dst, opts)
return err // Will overwrite itself.
})
return
}
// Write implements FileOperations.Write.
@@ -257,15 +275,20 @@ func (f *overlayFileOperations) Write(ctx context.Context, file *File, src userm
return f.upper.FileOperations.Write(ctx, f.upper, src, offset)
}
// ReadFrom implements FileOperations.ReadFrom.
func (f *overlayFileOperations) ReadFrom(ctx context.Context, file *File, src *File, opts SpliceOpts) (n int64, err error) {
// See above; f.upper must be non-nil.
return f.upper.FileOperations.ReadFrom(ctx, f.upper, src, opts)
}
// Fsync implements FileOperations.Fsync.
func (f *overlayFileOperations) Fsync(ctx context.Context, file *File, start, end int64, syncType SyncType) error {
var err error
func (f *overlayFileOperations) Fsync(ctx context.Context, file *File, start, end int64, syncType SyncType) (err error) {
f.upperMu.Lock()
if f.upper != nil {
err = f.upper.FileOperations.Fsync(ctx, f.upper, start, end, syncType)
}
f.upperMu.Unlock()
if f.lower != nil {
if err == nil && f.lower != nil {
// N.B. Fsync on the lower filesystem can cause writes of file
// attributes (i.e. access time) despite the fact that we must
// treat the lower filesystem as read-only.
@@ -277,15 +300,14 @@ func (f *overlayFileOperations) Fsync(ctx context.Context, file *File, start, en
}
// Flush implements FileOperations.Flush.
func (f *overlayFileOperations) Flush(ctx context.Context, file *File) error {
func (f *overlayFileOperations) Flush(ctx context.Context, file *File) (err error) {
// Flush whatever handles we have.
var err error
f.upperMu.Lock()
if f.upper != nil {
err = f.upper.FileOperations.Flush(ctx, f.upper)
}
f.upperMu.Unlock()
if f.lower != nil {
if err == nil && f.lower != nil {
err = f.lower.FileOperations.Flush(ctx, f.lower)
}
return err
@@ -329,6 +351,7 @@ func (*overlayFileOperations) ConfigureMMap(ctx context.Context, file *File, opt
if !o.isMappableLocked() {
return syserror.ENODEV
}
// FIXME(jamieliu): This is a copy/paste of fsutil.GenericConfigureMMap,
// which we can't use because the overlay implementation is in package fs,
// so depending on fs/fsutil would create a circular dependency. Move
+1
View File
@@ -38,6 +38,7 @@ type TestFileOperations struct {
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
}
+16
View File
@@ -223,6 +223,20 @@ func (FileNoIoctl) Ioctl(ctx context.Context, io usermem.IO, args arch.SyscallAr
return 0, syserror.ENOTTY
}
// FileNoSplice implements fs.FileOperations.ReadFrom and
// fs.FileOperations.WriteTo for files that don't support splice.
type FileNoSplice struct{}
// WriteTo implements fs.FileOperations.WriteTo.
func (FileNoSplice) WriteTo(context.Context, *fs.File, *fs.File, fs.SpliceOpts) (int64, error) {
return 0, syserror.ENOSYS
}
// ReadFrom implements fs.FileOperations.ReadFrom.
func (FileNoSplice) ReadFrom(context.Context, *fs.File, *fs.File, fs.SpliceOpts) (int64, error) {
return 0, syserror.ENOSYS
}
// DirFileOperations implements most of fs.FileOperations for directories,
// except for Readdir and UnstableAttr which the embedding type must implement.
type DirFileOperations struct {
@@ -233,6 +247,7 @@ type DirFileOperations struct {
FileNoopFlush
FileNoopFsync
FileNoopRelease
FileNoSplice
}
// Read implements fs.FileOperations.Read
@@ -303,6 +318,7 @@ type NoReadWriteFile struct {
FileNoWrite `state:"nosave"`
FileNotDirReaddir `state:"nosave"`
FileUseInodeUnstableAttr `state:"nosave"`
FileNoSplice `state:"nosave"`
}
var _ fs.FileOperations = (*NoReadWriteFile)(nil)
+2 -1
View File
@@ -250,16 +250,17 @@ func (i *InodeSimpleExtendedAttributes) Listxattr(_ *fs.Inode) (map[string]struc
//
// +stateify savable
type staticFile struct {
waiter.AlwaysReady `state:"nosave"`
FileGenericSeek `state:"nosave"`
FileNoIoctl `state:"nosave"`
FileNoMMap `state:"nosave"`
FileNoSplice `state:"nosave"`
FileNoopFsync `state:"nosave"`
FileNoopFlush `state:"nosave"`
FileNoopRelease `state:"nosave"`
FileNoopWrite `state:"nosave"`
FileNotDirReaddir `state:"nosave"`
FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
FileStaticContentReader
}
+3 -2
View File
@@ -46,8 +46,9 @@ var (
//
// +stateify savable
type fileOperations struct {
fsutil.FileNoIoctl `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoSplice `state:"nosplice"`
waiter.AlwaysReady `state:"nosave"`
// inodeOperations is the inodeOperations backing the file. It is protected
// by a reference held by File.Dirent.Inode which is stable until
+1
View File
@@ -37,6 +37,7 @@ import (
// +stateify savable
type fileOperations struct {
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoSplice `state:"nosplice"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
+10
View File
@@ -171,11 +171,21 @@ func (i *Inotify) Read(ctx context.Context, _ *File, dst usermem.IOSequence, _ i
return writeLen, nil
}
// WriteTo implements FileOperations.WriteTo.
func (*Inotify) WriteTo(context.Context, *File, *File, SpliceOpts) (int64, error) {
return 0, syserror.ENOSYS
}
// Fsync implements FileOperations.Fsync.
func (*Inotify) Fsync(context.Context, *File, int64, int64, SyncType) error {
return syserror.EINVAL
}
// ReadFrom implements FileOperations.ReadFrom.
func (*Inotify) ReadFrom(context.Context, *File, *File, SpliceOpts) (int64, error) {
return 0, syserror.ENOSYS
}
// Flush implements FileOperations.Flush.
func (*Inotify) Flush(context.Context, *File) error {
return nil
+2 -1
View File
@@ -77,16 +77,17 @@ func (i *execArgInode) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.
// +stateify savable
type execArgFile struct {
waiter.AlwaysReady `state:"nosave"`
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopWrite `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
// arg is the type of exec argument this file contains.
arg execArgType
+2 -1
View File
@@ -60,15 +60,16 @@ func (i *rpcInetInode) GetFile(ctx context.Context, dirent *fs.Dirent, flags fs.
// rpcInetFile implements fs.FileOperations as RPCs.
type rpcInetFile struct {
waiter.AlwaysReady `state:"nosave"`
fsutil.FileGenericSeek `state:"nosave"`
fsutil.FileNoIoctl `state:"nosave"`
fsutil.FileNoMMap `state:"nosave"`
fsutil.FileNoSplice `state:"nosave"`
fsutil.FileNoopFlush `state:"nosave"`
fsutil.FileNoopFsync `state:"nosave"`
fsutil.FileNoopRelease `state:"nosave"`
fsutil.FileNotDirReaddir `state:"nosave"`
fsutil.FileUseInodeUnstableAttr `state:"nosave"`
waiter.AlwaysReady `state:"nosave"`
inode *rpcInetInode
}

Some files were not shown because too many files have changed in this diff Show More