summaryrefslogtreecommitdiffhomepage
path: root/pkg/sentry/fs/file.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/sentry/fs/file.go')
-rw-r--r--pkg/sentry/fs/file.go62
1 files changed, 54 insertions, 8 deletions
diff --git a/pkg/sentry/fs/file.go b/pkg/sentry/fs/file.go
index f2683bbd2..6d93ef760 100644
--- a/pkg/sentry/fs/file.go
+++ b/pkg/sentry/fs/file.go
@@ -16,6 +16,7 @@ package fs
import (
"math"
+ "sync"
"sync/atomic"
"gvisor.googlesource.com/gvisor/pkg/amutex"
@@ -72,9 +73,15 @@ type File struct {
// other files via the Dirent cache.
Dirent *Dirent
+ // flagsMu protects flags and async below.
+ flagsMu sync.Mutex `state:"nosave"`
+
// flags are the File's flags. Setting or getting flags is fully atomic
// and is not protected by mu (below).
- flags atomic.Value `state:".(FileFlags)"`
+ flags FileFlags
+
+ // async handles O_ASYNC notifications.
+ async FileAsync
// mu is dual-purpose: first, to make read(2) and write(2) thread-safe
// in conformity with POSIX, and second, to cancel operations before they
@@ -99,8 +106,8 @@ func NewFile(ctx context.Context, dirent *Dirent, flags FileFlags, fops FileOper
UniqueID: uniqueid.GlobalFromContext(ctx),
Dirent: dirent,
FileOperations: fops,
+ flags: flags,
}
- f.flags.Store(flags)
f.mu.Init()
return f
}
@@ -117,22 +124,40 @@ func (f *File) DecRef() {
// Release a reference on the Dirent.
f.Dirent.DecRef()
+
+ f.flagsMu.Lock()
+ if f.flags.Async && f.async != nil {
+ f.async.Unregister(f)
+ }
+ f.flagsMu.Unlock()
})
}
// Flags atomically loads the File's flags.
func (f *File) Flags() FileFlags {
- return f.flags.Load().(FileFlags)
+ f.flagsMu.Lock()
+ flags := f.flags
+ f.flagsMu.Unlock()
+ return flags
}
// SetFlags atomically changes the File's flags to the values contained
// in newFlags. See SettableFileFlags for values that can be set.
func (f *File) SetFlags(newFlags SettableFileFlags) {
- flags := f.flags.Load().(FileFlags)
- flags.Direct = newFlags.Direct
- flags.NonBlocking = newFlags.NonBlocking
- flags.Append = newFlags.Append
- f.flags.Store(flags)
+ f.flagsMu.Lock()
+ f.flags.Direct = newFlags.Direct
+ f.flags.NonBlocking = newFlags.NonBlocking
+ f.flags.Append = newFlags.Append
+ if f.async != nil {
+ if newFlags.Async && !f.flags.Async {
+ f.async.Register(f)
+ }
+ if !newFlags.Async && f.flags.Async {
+ f.async.Unregister(f)
+ }
+ }
+ f.flags.Async = newFlags.Async
+ f.flagsMu.Unlock()
}
// Offset atomically loads the File's offset.
@@ -361,6 +386,27 @@ func (f *File) Msync(ctx context.Context, mr memmap.MappableRange) error {
return f.Fsync(ctx, int64(mr.Start), int64(mr.End-1), SyncData)
}
+// A FileAsync sends signals to its owner when w is ready for IO.
+type FileAsync interface {
+ Register(w waiter.Waitable)
+ Unregister(w waiter.Waitable)
+}
+
+// Async gets the stored FileAsync or creates a new one with the supplied
+// function. If the supplied function is nil, no FileAsync is created and the
+// current value is returned.
+func (f *File) Async(newAsync func() FileAsync) FileAsync {
+ f.flagsMu.Lock()
+ defer f.flagsMu.Unlock()
+ if f.async == nil && newAsync != nil {
+ f.async = newAsync()
+ if f.flags.Async {
+ f.async.Register(f)
+ }
+ }
+ return f.async
+}
+
// FileReader implements io.Reader and io.ReaderAt.
type FileReader struct {
// Ctx is the context for the file reader.