Commit ebe18c90 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Ensure readdir replies are always under 4096 bytes.

Use channel and goroutine for getting names from the directory.
parent 1989e683
......@@ -116,7 +116,7 @@ func (self *DummyFuseFile) Release() {
func (self *DummyFuseFile) Fsync(*FsyncIn) (code Status) {
return ENOSYS
}
func (self *DummyFuseFile) ReadDir(input *ReadIn) (*DEntryList, Status) {
func (self *DummyFuseFile) ReadDir(input *ReadIn) (*DirEntryList, Status) {
return nil, ENOSYS
}
......
......@@ -536,12 +536,18 @@ func doFsyncDir(state *MountState, header *InHeader, input *FsyncIn) (code Statu
////////////////////////////////////////////////////////////////
// DentryList.
func (de *DEntryList) AddString(name string, inode uint64, mode uint32) {
de.Add([]byte(name), inode, mode)
func NewDirEntryList(max int) *DirEntryList {
return &DirEntryList{maxSize: max}
}
func (de *DEntryList) Add(name []byte, inode uint64, mode uint32) {
func (de *DirEntryList) AddString(name string, inode uint64, mode uint32) bool {
return de.Add([]byte(name), inode, mode)
}
func (de *DirEntryList) Add(name []byte, inode uint64, mode uint32) bool {
lastLen := de.buf.Len()
de.offset++
dirent := new(Dirent)
dirent.Off = de.offset
dirent.Ino = inode
......@@ -558,8 +564,15 @@ func (de *DEntryList) Add(name []byte, inode uint64, mode uint32) {
if padding < 8 {
de.buf.Write(make([]byte, padding))
}
if de.buf.Len() > de.maxSize {
de.buf.Truncate(lastLen)
de.offset--
return false
}
return true
}
func (de *DEntryList) Bytes() []byte {
func (de *DirEntryList) Bytes() []byte {
return de.buf.Bytes()
}
......@@ -77,8 +77,8 @@ func (self *PassThroughFuse) OpenDir(name string) (fuseFile RawFuseDir, status S
if err != nil {
return nil, OsErrorToFuseError(err)
}
return &PassThroughFile{file: f}, OK
p := NewPassThroughDir(f)
return p, OK
}
func (self *PassThroughFuse) Open(name string, flags uint32) (fuseFile RawFuseFile, status Status) {
......@@ -184,28 +184,69 @@ func (self *PassThroughFile) Fsync(*FsyncIn) (code Status) {
return Status(syscall.Fsync(self.file.Fd()))
}
func (self *PassThroughFile) ReadDir(input *ReadIn) (*DEntryList, Status) {
list := new(DEntryList)
// TODO - How to try accomodating the requested Size?
// (typically: 4096.)
////////////////////////////////////////////////////////////////
// To test chunked reads, just return fetch 20 at a time.
fis, err := self.file.Readdir(20)
if err != nil {
return nil, OsErrorToFuseError(err)
type PassThroughDir struct {
directoryChannel chan *os.FileInfo
directoryError os.Error
shipped int
exported int
leftOver *os.FileInfo
}
func NewPassThroughDir(file *os.File) *PassThroughDir {
self := new(PassThroughDir)
self.directoryChannel = make(chan *os.FileInfo, 500)
go func() {
for {
want := 500
infos, err := file.Readdir(want)
for i, _ := range infos {
self.directoryChannel <- &infos[i]
}
if len(infos) < want {
break
}
if err != nil {
self.directoryError = err
break
}
}
close(self.directoryChannel)
file.Close()
}()
return self
}
func (self *PassThroughDir) ReadDir(input *ReadIn) (*DirEntryList, Status) {
list := NewDirEntryList(int(input.Size))
if self.leftOver != nil {
success := list.AddString(self.leftOver.Name, self.leftOver.Ino, self.leftOver.Mode)
self.exported++
if !success {
panic("No space for single entry.")
}
self.leftOver = nil
}
for _, val := range fis {
list.AddString(val.Name, val.Ino, val.Mode)
for {
fi := <-self.directoryChannel
if fi == nil {
break
}
if !list.AddString(fi.Name, fi.Ino, fi.Mode) {
self.leftOver = fi
break
}
}
return list, OK
return list, OsErrorToFuseError(self.directoryError)
}
func (self *PassThroughFile) ReleaseDir() {
self.file.Close()
func (self *PassThroughDir) ReleaseDir() {
close(self.directoryChannel)
}
func (self *PassThroughFile) FsyncDir(input *FsyncIn) (code Status) {
func (self *PassThroughDir) FsyncDir(input *FsyncIn) (code Status) {
return ENOSYS
}
......@@ -556,7 +556,7 @@ func (self *testCase) testLargeDirRead() {
total := 0
readSet := make(map[string] bool)
for {
namesRead, err := dir.Readdirnames(20)
namesRead, err := dir.Readdirnames(200)
if err != nil {
self.tester.Errorf("readdir err %v %v", err, namesRead)
}
......@@ -573,15 +573,13 @@ func (self *testCase) testLargeDirRead() {
if total != created {
self.tester.Errorf("readdir mismatch got %v wanted %v", total, created)
}
for k, _ := range(nameSet) {
_, ok := readSet[k]
if !ok {
self.tester.Errorf("Name %v not found", k)
self.tester.Errorf("Name %v not found in output", k)
}
}
dir.Close()
os.RemoveAll(subdir)
......
......@@ -536,15 +536,16 @@ type RawFuseFile interface {
}
type RawFuseDir interface {
ReadDir(input *ReadIn) (*DEntryList, Status)
ReadDir(input *ReadIn) (*DirEntryList, Status)
ReleaseDir()
FsyncDir(input *FsyncIn) (code Status)
}
// Should make interface ?
type DEntryList struct {
type DirEntryList struct {
buf bytes.Buffer
offset uint64
maxSize int
}
type PathFilesystem interface {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment