)
type TorrentFS struct {
- Client *torrent.Client
- destroyed chan struct{}
- mu sync.Mutex
+ Client *torrent.Client
+ destroyed chan struct{}
+ mu sync.Mutex
+ blockedReads int
+ event sync.Cond
}
var (
}
func blockingRead(fs *TorrentFS, t torrent.Torrent, off int64, p []byte, intr fusefs.Intr) (n int, err fuse.Error) {
+ fs.mu.Lock()
+ fs.blockedReads++
+ fs.event.Broadcast()
+ fs.mu.Unlock()
var (
_n int
_err fuse.Error
case <-intr:
err = fuse.EINTR
}
+ fs.mu.Lock()
+ fs.blockedReads--
+ fs.event.Broadcast()
+ fs.mu.Unlock()
return
}
Client: cl,
destroyed: make(chan struct{}),
}
+ fs.event.L = &fs.mu
return fs
}
return
}
+// Unmount without first killing the FUSE connection while there are FUSE
+// operations blocked inside the filesystem code.
func TestUnmountWedged(t *testing.T) {
layout, err := newGreetingLayout()
if err != nil {
NoDefaultBlocklist: true,
})
defer client.Stop()
- log.Printf("%+v", *layout.Metainfo)
+ t.Logf("%+v", *layout.Metainfo)
client.AddTorrent(layout.Metainfo)
fs := New(client)
fuseConn, err := fuse.Mount(layout.MountDir)
}
t.Fatal(err)
}
+ <-fuseConn.Ready
+ if err := fuseConn.MountError; err != nil {
+ t.Fatal(err)
+ }
go func() {
server := fusefs.Server{
FS: fs,
Debug: func(msg interface{}) {
- log.Print(msg)
+ t.Log(msg)
},
}
server.Serve(fuseConn)
}()
- <-fuseConn.Ready
- if err := fuseConn.MountError; err != nil {
- log.Fatal(err)
- }
+ // Read the greeting file, though it will never be available. This should
+ // "wedge" FUSE, requiring the fs object to be forcibly destroyed. The
+ // read call will return with a FS error.
go func() {
- ioutil.ReadFile(filepath.Join(layout.MountDir, layout.Metainfo.Info.Name))
+ _, err := ioutil.ReadFile(filepath.Join(layout.MountDir, layout.Metainfo.Info.Name))
+ if err == nil {
+ t.Fatal("expected error reading greeting")
+ }
}()
- // time.Sleep(time.Second)
+
+ // Wait until the read has blocked inside the filesystem code.
+ fs.mu.Lock()
+ for fs.blockedReads != 1 {
+ fs.event.Wait()
+ }
+ fs.mu.Unlock()
+
fs.Destroy()
- // time.Sleep(time.Second)
- err = fuse.Unmount(layout.MountDir)
- if err != nil {
- log.Print(err)
+
+ for {
+ err = fuse.Unmount(layout.MountDir)
+ if err != nil {
+ t.Logf("error unmounting: %s", err)
+ } else {
+ break
+ }
}
+
err = fuseConn.Close()
if err != nil {
- t.Log(err)
+ t.Fatalf("error closing fuse conn: %s", err)
}
}
if err != nil {
t.Fatal(err)
}
+ defer layout.Destroy()
seeder, err := torrent.NewClient(&torrent.Config{
DataDir: layout.Completed,
DisableTrackers: true,