diff --git a/cephfs/directory.go b/cephfs/directory.go index 774c983..bc8b18e 100644 --- a/cephfs/directory.go +++ b/cephfs/directory.go @@ -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 +} diff --git a/cephfs/directory_test.go b/cephfs/directory_test.go index e3d3f06..0f7bea3 100644 --- a/cephfs/directory_test.go +++ b/cephfs/directory_test.go @@ -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) + }) +}