github.com/grailbio/base@v0.0.11/file/fsnodefuse/dirstream.go (about) 1 package fsnodefuse 2 3 import ( 4 "context" 5 "io" 6 "syscall" 7 8 "github.com/grailbio/base/errors" 9 "github.com/grailbio/base/file/fsnode" 10 "github.com/grailbio/base/log" 11 "github.com/hanwen/go-fuse/v2/fs" 12 "github.com/hanwen/go-fuse/v2/fuse" 13 ) 14 15 type dirStream struct { 16 ctx context.Context 17 dir *dirInode 18 entries []fsnode.T 19 // prev is the node of the previous entry, i.e. the node of the most recent 20 // entry returned by Next. We cache this node to service LOOKUP operations 21 // that go-fuse issues when servicing READDIRPLUS. See lookupCache. 22 prev fsnode.T 23 } 24 25 var _ fs.DirStream = (*dirStream)(nil) 26 27 // newDirStream returns a dirStream whose entries are the children of dir. 28 // Children are loaded eagerly so that any errors are reported before any 29 // entries are returned by the stream. If Next returns a non-OK Errno after 30 // a call that returned an OK Errno, the READDIR operation returns an EIO, 31 // regardless of the returned Errno. See 32 // https://github.com/hanwen/go-fuse/issues/436 . 33 func newDirStream( 34 ctx context.Context, 35 dir *dirInode, 36 ) (_ *dirStream, err error) { 37 var ( 38 entries []fsnode.T 39 iter = dir.n.Children() 40 ) 41 defer errors.CleanUpCtx(ctx, iter.Close, &err) 42 for { 43 n, err := iter.Next(ctx) 44 if err == io.EOF { 45 return &dirStream{ 46 ctx: ctx, 47 dir: dir, 48 entries: entries, 49 }, nil 50 } 51 if err != nil { 52 return nil, err 53 } 54 entries = append(entries, n) 55 } 56 } 57 58 func (d *dirStream) HasNext() bool { 59 return len(d.entries) != 0 60 } 61 62 func (d *dirStream) Next() (_ fuse.DirEntry, errno syscall.Errno) { 63 defer handlePanicErrno(&errno) 64 var next fsnode.T 65 next, d.entries = d.entries[0], d.entries[1:] 66 if d.prev != nil { 67 d.dir.readdirplusCache.Drop(d.prev) 68 } 69 d.prev = next 70 d.dir.readdirplusCache.Put(next) 71 name := next.Info().Name() 72 return fuse.DirEntry{ 73 Name: name, 74 Mode: mode(next), 75 Ino: hashIno(d.dir, name), 76 }, fs.OK 77 } 78 79 func (d *dirStream) Close() { 80 var err error 81 defer handlePanicErr(&err) 82 defer func() { 83 if err != nil { 84 log.Error.Printf("fsnodefuse.dirStream: error on close: %v", err) 85 } 86 }() 87 if d.prev != nil { 88 d.dir.readdirplusCache.Drop(d.prev) 89 d.prev = nil 90 } 91 }