cephfs: add ReadDir implementing ceph_readdir_r function

Adds a method to ReadDir that implements ceph_readdir_r returning
a single dir entry at at time.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
John Mulligan 2020-02-14 14:50:57 -05:00 committed by John Mulligan
parent e98648f76f
commit fd275e2b9a
2 changed files with 104 additions and 0 deletions

View File

@ -47,3 +47,48 @@ func (mount *MountInfo) OpenDir(path string) (*Directory, error) {
func (dir *Directory) Close() error {
return getError(C.ceph_closedir(dir.mount.mount, dir.dir))
}
// Inode represents an inode number in the file system.
type Inode uint64
// DirEntry represents an entry within a directory.
type DirEntry struct {
inode Inode
name string
}
// Name returns the directory entry's name.
func (d *DirEntry) Name() string {
return d.name
}
// Inode returns the directory entry's inode number.
func (d *DirEntry) Inode() Inode {
return d.inode
}
// toDirEntry converts a c struct dirent to our go wrapper.
func toDirEntry(de *C.struct_dirent) *DirEntry {
return &DirEntry{
inode: Inode(de.d_ino),
name: C.GoString(&de.d_name[0]),
}
}
// ReadDir reads a single directory entry from the open Directory.
// A nil DirEntry pointer will be returned when the Directory stream has been
// exhausted.
//
// Implements:
// int ceph_readdir_r(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, struct dirent *de);
func (dir *Directory) ReadDir() (*DirEntry, error) {
var de C.struct_dirent
ret := C.ceph_readdir_r(dir.mount.mount, dir.dir, &de)
if ret < 0 {
return nil, getError(ret)
}
if ret == 0 {
return nil, nil // End-of-stream
}
return toDirEntry(&de), nil
}

View File

@ -36,3 +36,62 @@ func TestOpenCloseDir(t *testing.T) {
assert.Error(t, err)
assert.Nil(t, dir)
}
func TestReadDir(t *testing.T) {
mount := fsConnect(t)
defer fsDisconnect(t, mount)
dir1 := "/base"
err := mount.MakeDir(dir1, 0755)
assert.NoError(t, err)
defer func() { assert.NoError(t, mount.RemoveDir(dir1)) }()
subdirs := []string{"a", "bb", "ccc", "dddd"}
for _, s := range subdirs {
spath := dir1 + "/" + s
err = mount.MakeDir(spath, 0755)
assert.NoError(t, err)
defer func(d string) {
assert.NoError(t, mount.RemoveDir(d))
}(spath)
}
t.Run("root", func(t *testing.T) {
dir, err := mount.OpenDir("/")
assert.NoError(t, err)
assert.NotNil(t, dir)
defer func() { assert.NoError(t, dir.Close()) }()
found := []string{}
for {
entry, err := dir.ReadDir()
assert.NoError(t, err)
if entry == nil {
break
}
assert.NotEqual(t, Inode(0), entry.Inode())
assert.NotEqual(t, "", entry.Name())
found = append(found, entry.Name())
}
assert.Contains(t, found, "base")
})
t.Run("dir1", func(t *testing.T) {
dir, err := mount.OpenDir(dir1)
assert.NoError(t, err)
assert.NotNil(t, dir)
defer func() { assert.NoError(t, dir.Close()) }()
found := []string{}
for {
entry, err := dir.ReadDir()
assert.NoError(t, err)
if entry == nil {
break
}
assert.NotEqual(t, Inode(0), entry.Inode())
assert.NotEqual(t, "", entry.Name())
found = append(found, entry.Name())
}
assert.Subset(t, found, subdirs)
})
}