123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209 |
- // Copyright 2015 Google Inc. All Rights Reserved.
- //
- // 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 interruptfs
- import (
- "fmt"
- "os"
- "sync"
- "golang.org/x/net/context"
- "github.com/jacobsa/fuse"
- "github.com/jacobsa/fuse/fuseops"
- "github.com/jacobsa/fuse/fuseutil"
- )
- var rootAttrs = fuseops.InodeAttributes{
- Nlink: 1,
- Mode: os.ModeDir | 0777,
- }
- const fooID = fuseops.RootInodeID + 1
- var fooAttrs = fuseops.InodeAttributes{
- Nlink: 1,
- Mode: 0777,
- Size: 1234,
- }
- // A file system containing exactly one file, named "foo". ReadFile and
- // FlushFile ops can be made to hang until interrupted. Exposes a method for
- // synchronizing with the arrival of a read or a flush.
- //
- // Must be created with New.
- type InterruptFS struct {
- fuseutil.NotImplementedFileSystem
- mu sync.Mutex
- blockForReads bool // GUARDED_BY(mu)
- blockForFlushes bool // GUARDED_BY(mu)
- // Must hold the mutex when closing these.
- readReceived chan struct{}
- flushReceived chan struct{}
- }
- func New() (fs *InterruptFS) {
- fs = &InterruptFS{
- readReceived: make(chan struct{}),
- flushReceived: make(chan struct{}),
- }
- return
- }
- ////////////////////////////////////////////////////////////////////////
- // Public interface
- ////////////////////////////////////////////////////////////////////////
- // Block until the first read is received.
- func (fs *InterruptFS) WaitForFirstRead() {
- <-fs.readReceived
- }
- // Block until the first flush is received.
- func (fs *InterruptFS) WaitForFirstFlush() {
- <-fs.flushReceived
- }
- // Enable blocking until interrupted for the next (and subsequent) read ops.
- func (fs *InterruptFS) EnableReadBlocking() {
- fs.mu.Lock()
- defer fs.mu.Unlock()
- fs.blockForReads = true
- }
- // Enable blocking until interrupted for the next (and subsequent) flush ops.
- func (fs *InterruptFS) EnableFlushBlocking() {
- fs.mu.Lock()
- defer fs.mu.Unlock()
- fs.blockForFlushes = true
- }
- ////////////////////////////////////////////////////////////////////////
- // FileSystem methods
- ////////////////////////////////////////////////////////////////////////
- func (fs *InterruptFS) StatFS(
- ctx context.Context,
- op *fuseops.StatFSOp) (err error) {
- return
- }
- func (fs *InterruptFS) LookUpInode(
- ctx context.Context,
- op *fuseops.LookUpInodeOp) (err error) {
- // We support only one parent.
- if op.Parent != fuseops.RootInodeID {
- err = fmt.Errorf("Unexpected parent: %v", op.Parent)
- return
- }
- // We support only one name.
- if op.Name != "foo" {
- err = fuse.ENOENT
- return
- }
- // Fill in the response.
- op.Entry.Child = fooID
- op.Entry.Attributes = fooAttrs
- return
- }
- func (fs *InterruptFS) GetInodeAttributes(
- ctx context.Context,
- op *fuseops.GetInodeAttributesOp) (err error) {
- switch op.Inode {
- case fuseops.RootInodeID:
- op.Attributes = rootAttrs
- case fooID:
- op.Attributes = fooAttrs
- default:
- err = fmt.Errorf("Unexpected inode ID: %v", op.Inode)
- return
- }
- return
- }
- func (fs *InterruptFS) OpenFile(
- ctx context.Context,
- op *fuseops.OpenFileOp) (err error) {
- return
- }
- func (fs *InterruptFS) ReadFile(
- ctx context.Context,
- op *fuseops.ReadFileOp) (err error) {
- fs.mu.Lock()
- shouldBlock := fs.blockForReads
- // Signal that a read has been received, if this is the first.
- select {
- case <-fs.readReceived:
- default:
- close(fs.readReceived)
- }
- fs.mu.Unlock()
- // Wait for cancellation if enabled.
- if shouldBlock {
- done := ctx.Done()
- if done == nil {
- panic("Expected non-nil channel.")
- }
- <-done
- err = ctx.Err()
- }
- return
- }
- func (fs *InterruptFS) FlushFile(
- ctx context.Context,
- op *fuseops.FlushFileOp) (err error) {
- fs.mu.Lock()
- shouldBlock := fs.blockForFlushes
- // Signal that a flush has been received, if this is the first.
- select {
- case <-fs.flushReceived:
- default:
- close(fs.flushReceived)
- }
- fs.mu.Unlock()
- // Wait for cancellation if enabled.
- if shouldBlock {
- done := ctx.Done()
- if done == nil {
- panic("Expected non-nil channel.")
- }
- <-done
- err = ctx.Err()
- }
- return
- }
|