diff options
Diffstat (limited to 'pkg/sentry/fs/file.go')
-rw-r--r-- | pkg/sentry/fs/file.go | 62 |
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. |