// Copyright 2019 the Go-FUSE Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package fs import ( "sync" "syscall" "unsafe" "github.com/hanwen/go-fuse/v2/fuse" ) type loopbackDirStream struct { buf []byte todo []byte // Protects fd so we can guard against double close mu sync.Mutex fd int } // NewLoopbackDirStream open a directory for reading as a DirStream func NewLoopbackDirStream(name string) (DirStream, syscall.Errno) { fd, err := syscall.Open(name, syscall.O_DIRECTORY, 0755) if err != nil { return nil, ToErrno(err) } ds := &loopbackDirStream{ buf: make([]byte, 4096), fd: fd, } if err := ds.load(); err != 0 { ds.Close() return nil, err } return ds, OK } func (ds *loopbackDirStream) Close() { ds.mu.Lock() defer ds.mu.Unlock() if ds.fd != -1 { syscall.Close(ds.fd) ds.fd = -1 } } func (ds *loopbackDirStream) HasNext() bool { ds.mu.Lock() defer ds.mu.Unlock() return len(ds.todo) > 0 } // Like syscall.Dirent, but without the [256]byte name. type dirent struct { Ino uint64 Off int64 Reclen uint16 Type uint8 Name [1]uint8 // align to 4 bytes for 32 bits. } func (ds *loopbackDirStream) Next() (fuse.DirEntry, syscall.Errno) { ds.mu.Lock() defer ds.mu.Unlock() // We can't use syscall.Dirent here, because it declares a // [256]byte name, which may run beyond the end of ds.todo. // when that happens in the race detector, it causes a panic // "converted pointer straddles multiple allocations" de := (*dirent)(unsafe.Pointer(&ds.todo[0])) nameBytes := ds.todo[unsafe.Offsetof(dirent{}.Name):de.Reclen] ds.todo = ds.todo[de.Reclen:] // After the loop, l contains the index of the first '\0'. l := 0 for l = range nameBytes { if nameBytes[l] == 0 { break } } nameBytes = nameBytes[:l] result := fuse.DirEntry{ Ino: de.Ino, Mode: (uint32(de.Type) << 12), Name: string(nameBytes), } return result, ds.load() } func (ds *loopbackDirStream) load() syscall.Errno { if len(ds.todo) > 0 { return OK } n, err := syscall.Getdents(ds.fd, ds.buf) if err != nil { return ToErrno(err) } ds.todo = ds.buf[:n] return OK }