Skip to content

Commit db87cbf

Browse files
authored
Merge pull request gluster#26 from jfontan/implement-readdir
Implement File.Readdir and File.Readdirnames
2 parents 833cee3 + 2114dbe commit db87cbf

File tree

3 files changed

+246
-5
lines changed

3 files changed

+246
-5
lines changed

gfapi/fd.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package gfapi
88
// #include <sys/stat.h>
99
import "C"
1010
import (
11+
"os"
1112
"syscall"
1213
"unsafe"
1314
)
@@ -190,3 +191,71 @@ func (fd *Fd) Fremovexattr(attr string) error {
190191
}
191192
return err
192193
}
194+
195+
func direntName(dirent *syscall.Dirent) string {
196+
name := make([]byte, 0, len(dirent.Name))
197+
for i, c := range dirent.Name {
198+
if c == 0 || i > 255 {
199+
break
200+
}
201+
202+
name = append(name, byte(c))
203+
}
204+
205+
return string(name)
206+
}
207+
208+
// Readdir returns the information of files in a directory.
209+
//
210+
// n is the maximum number of items to return. If there are more items than
211+
// the maximum they can be obtained in successive calls. If maximum is 0
212+
// then all the items will be returned.
213+
func (fd *Fd) Readdir(n int) ([]os.FileInfo, error) {
214+
var (
215+
stat syscall.Stat_t
216+
files []os.FileInfo
217+
statP = (*C.struct_stat)(unsafe.Pointer(&stat))
218+
)
219+
220+
for i := 0; n == 0 || i < n; i++ {
221+
d, err := C.glfs_readdirplus(fd.fd, statP)
222+
if err != nil {
223+
return nil, err
224+
}
225+
226+
dirent := (*syscall.Dirent)(unsafe.Pointer(d))
227+
if dirent == nil {
228+
break
229+
}
230+
231+
name := direntName(dirent)
232+
file := fileInfoFromStat(&stat, name)
233+
files = append(files, file)
234+
}
235+
236+
return files, nil
237+
}
238+
239+
// Readdirnames returns the names of files in a directory.
240+
//
241+
// n is the maximum number of items to return and works the same way as Readdir.
242+
func (fd *Fd) Readdirnames(n int) ([]string, error) {
243+
var names []string
244+
245+
for i := 0; n == 0 || i < n; i++ {
246+
d, err := C.glfs_readdir(fd.fd)
247+
if err != nil {
248+
return nil, err
249+
}
250+
251+
dirent := (*syscall.Dirent)(unsafe.Pointer(d))
252+
if dirent == nil {
253+
break
254+
}
255+
256+
name := direntName(dirent)
257+
names = append(names, name)
258+
}
259+
260+
return names, nil
261+
}

gfapi/file.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,20 @@ func (f *File) ReadAt(b []byte, off int64) (int, error) {
8787
return f.Fd.Pread(b, off)
8888
}
8989

90-
// Readdir has not been implemented yet
90+
// Readdir returns the information of files in a directory.
91+
//
92+
// n is the maximum number of items to return. If there are more items than
93+
// the maximum they can be obtained in successive calls. If maximum is 0
94+
// then all the items will be returned.
9195
func (f *File) Readdir(n int) ([]os.FileInfo, error) {
92-
return nil, errors.New("Readdir has not been implemented yet")
96+
return f.Fd.Readdir(n)
9397
}
9498

95-
// Readdirnames has not been implemented yet
99+
// Readdirnames returns the names of files in a directory.
100+
//
101+
// n is the maximum number of items to return and works the same way as Readdir.
96102
func (f *File) Readdirnames(n int) ([]string, error) {
97-
return nil, errors.New("Readdirnames has not been implemented yet")
103+
return f.Fd.Readdirnames(n)
98104
}
99105

100106
// Seek sets the offset for the next read or write on the file based on whence,

gfapi/gfapi_test.go

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package gfapi
33
import (
44
"os"
55
"path/filepath"
6+
"reflect"
67
"runtime"
8+
"sort"
79
"testing"
810
)
911

@@ -24,7 +26,7 @@ func TestInit(t *testing.T) {
2426
t.Fatalf("Failed to allocate variable")
2527
}
2628

27-
err := vol.Init("localhost", "test")
29+
err := vol.Init("test", "localhost")
2830
if err != nil {
2931
t.Fatalf("Failed to initialize volume. error: %v", err)
3032
}
@@ -248,9 +250,173 @@ func TestStatvfs(t *testing.T) {
248250
}
249251
}
250252

253+
func TestReaddir(t *testing.T) {
254+
tmpDir, clean := setupReaddir(t)
255+
defer clean()
256+
257+
d, err := vol.Open(tmpDir)
258+
check(t, err == nil, "Open %q: %s", tmpDir, err)
259+
260+
info, err := d.Readdir(0)
261+
check(t, err == nil, "Readdir %q: %s", tmpDir, err)
262+
263+
err = d.Close()
264+
check(t, err == nil, "Close %q: %s", tmpDir, err)
265+
266+
check(t, len(info) == 4,
267+
"incorrect number of files %v != %v", len(info), 4)
268+
269+
files := map[string]os.FileInfo{
270+
"dir": nil,
271+
"file": nil,
272+
}
273+
274+
for _, d := range info {
275+
if d == nil {
276+
continue
277+
}
278+
if _, ok := files[d.Name()]; ok {
279+
files[d.Name()] = d
280+
}
281+
}
282+
283+
check(t, files["file"] != nil, "no info for file")
284+
check(t, files["file"].IsDir() == false, "file should not be a dir")
285+
check(t, files["file"].Size() == int64(len(data)),
286+
"incorrect file size %v != %v", files["file"].Size(), len(data))
287+
288+
check(t, files["dir"] != nil, "no info for dir")
289+
check(t, files["dir"].IsDir() == true, "dir should be a directory")
290+
check(t, files["dir"].Mode()&os.ModePerm == dirPerm,
291+
"incorrect dir mode %#o != %#o", files["dir"].Mode(), dirPerm)
292+
293+
// test readdir with limit
294+
295+
d, err = vol.Open(tmpDir)
296+
check(t, err == nil, "Open %q: %s", tmpDir, err)
297+
298+
info, err = d.Readdir(2)
299+
check(t, err == nil, "Readdir %q: %s", tmpDir, err)
300+
check(t, len(info) == 2, "should only read 2 files")
301+
302+
info, err = d.Readdir(2)
303+
check(t, err == nil, "Readdir %q: %s", tmpDir, err)
304+
check(t, len(info) == 2, "should only read 2 files")
305+
306+
info, err = d.Readdir(2)
307+
check(t, err == nil, "Readdir %q: %s", tmpDir, err)
308+
check(t, len(info) == 0, "should not read more files")
309+
310+
err = d.Close()
311+
check(t, err == nil, "Close %q: %s", tmpDir, err)
312+
}
313+
314+
func TestReaddirnames(t *testing.T) {
315+
tmpDir, clean := setupReaddir(t)
316+
defer clean()
317+
318+
d, err := vol.Open(tmpDir)
319+
check(t, err == nil, "Open %q: %s", tmpDir, err)
320+
321+
names, err := d.Readdirnames(0)
322+
check(t, err == nil, "Readdirnames %q: %s", tmpDir, err)
323+
324+
err = d.Close()
325+
check(t, err == nil, "Close %q: %s", tmpDir, err)
326+
327+
check(t, len(names) == 4,
328+
"incorrect number of files %v != %v", len(names), 4)
329+
330+
expected := []string{
331+
".",
332+
"..",
333+
"dir",
334+
"file",
335+
}
336+
337+
sort.Strings(names)
338+
check(t, reflect.DeepEqual(names, expected),
339+
"file names doesn't match %v != %v", names, expected)
340+
341+
// test readdirnames with limit
342+
343+
d, err = vol.Open(tmpDir)
344+
check(t, err == nil, "Open %q: %s", tmpDir, err)
345+
346+
var all []string
347+
348+
names, err = d.Readdirnames(2)
349+
check(t, err == nil, "Readdirnames %q: %s", tmpDir, err)
350+
check(t, len(names) == 2, "should only read 2 files")
351+
all = append(all, names...)
352+
353+
names, err = d.Readdirnames(2)
354+
check(t, err == nil, "Readdirnames %q: %s", tmpDir, err)
355+
check(t, len(names) == 2, "should only read 2 files")
356+
all = append(all, names...)
357+
358+
names, err = d.Readdirnames(2)
359+
check(t, err == nil, "Readdirnames %q: %s", tmpDir, err)
360+
check(t, len(names) == 0, "should not read more files")
361+
362+
err = d.Close()
363+
check(t, err == nil, "Close %q: %s", tmpDir, err)
364+
365+
check(t, len(all) == 4,
366+
"incorrect number of files %v != %v", len(all), 4)
367+
368+
sort.Strings(all)
369+
check(t, reflect.DeepEqual(all, expected),
370+
"file names doesn't match %v != %v", all, expected)
371+
}
372+
251373
func TestUnmount(t *testing.T) {
252374
err := vol.Unmount()
253375
if err != nil {
254376
t.Logf("Failed to unmount volume. Ret = %v", err)
255377
}
256378
}
379+
380+
var (
381+
dirPerm = os.FileMode(0700)
382+
data = []byte("data")
383+
)
384+
385+
func setupReaddir(t *testing.T) (string, func()) {
386+
tmpDir := "/test-gluster-readdir"
387+
err := vol.MkdirAll(tmpDir, 0777)
388+
check(t, err == nil, "MkdirAll %q: %s", tmpDir, err)
389+
390+
dir := filepath.Join(tmpDir, "dir")
391+
file := filepath.Join(tmpDir, "file")
392+
393+
err = vol.MkdirAll(dir, dirPerm)
394+
check(t, err == nil, "MkdirAll %q: %s", dir, err)
395+
396+
f, err := vol.Create(file)
397+
check(t, err == nil, "Create %q: %s", file, err)
398+
399+
n, err := f.Write(data)
400+
check(t, err == nil, "Write %q: %s", file, err)
401+
check(t, n == len(data), "write length incorrect, %v != %v", n, len(data))
402+
403+
_, err = f.Readdir(0)
404+
check(t, err != nil, "Readdir should fail with files, %v", file)
405+
406+
err = f.Close()
407+
check(t, err == nil, "Close %q: %s", file, err)
408+
409+
return tmpDir, func() {
410+
vol.Unlink(file)
411+
vol.Unlink(dir)
412+
vol.Unlink(tmpDir)
413+
}
414+
}
415+
416+
func check(t *testing.T, c bool, message string, args ...interface{}) {
417+
t.Helper()
418+
419+
if !c {
420+
t.Fatalf(message, args...)
421+
}
422+
}

0 commit comments

Comments
 (0)