2020-02-14 19:49:37 +00:00
|
|
|
package cephfs
|
|
|
|
|
|
|
|
/*
|
|
|
|
#cgo LDFLAGS: -lcephfs
|
|
|
|
#cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <cephfs/libcephfs.h>
|
|
|
|
*/
|
|
|
|
import "C"
|
|
|
|
|
|
|
|
import (
|
|
|
|
"unsafe"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Directory represents an open directory handle.
|
|
|
|
type Directory struct {
|
|
|
|
mount *MountInfo
|
|
|
|
dir *C.struct_ceph_dir_result
|
|
|
|
}
|
|
|
|
|
|
|
|
// OpenDir returns a new Directory handle open for I/O.
|
|
|
|
//
|
|
|
|
// Implements:
|
|
|
|
// int ceph_opendir(struct ceph_mount_info *cmount, const char *name, struct ceph_dir_result **dirpp);
|
|
|
|
func (mount *MountInfo) OpenDir(path string) (*Directory, error) {
|
|
|
|
var dir *C.struct_ceph_dir_result
|
|
|
|
|
|
|
|
cPath := C.CString(path)
|
|
|
|
defer C.free(unsafe.Pointer(cPath))
|
|
|
|
|
|
|
|
ret := C.ceph_opendir(mount.mount, cPath, &dir)
|
|
|
|
if ret != 0 {
|
|
|
|
return nil, getError(ret)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &Directory{
|
|
|
|
mount: mount,
|
|
|
|
dir: dir,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close the open directory handle.
|
|
|
|
//
|
|
|
|
// Implements:
|
|
|
|
// int ceph_closedir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp);
|
|
|
|
func (dir *Directory) Close() error {
|
|
|
|
return getError(C.ceph_closedir(dir.mount.mount, dir.dir))
|
|
|
|
}
|
2020-02-14 19:50:57 +00:00
|
|
|
|
|
|
|
// Inode represents an inode number in the file system.
|
|
|
|
type Inode uint64
|
|
|
|
|
2020-04-29 14:28:00 +00:00
|
|
|
// DType values are used to determine, when possible, the file type
|
|
|
|
// of a directory entry.
|
|
|
|
type DType uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DTypeBlk indicates a directory entry is a block device.
|
|
|
|
DTypeBlk = DType(C.DT_BLK)
|
|
|
|
// DTypeChr indicates a directory entry is a character device.
|
|
|
|
DTypeChr = DType(C.DT_CHR)
|
|
|
|
// DTypeDir indicates a directory entry is a directory.
|
|
|
|
DTypeDir = DType(C.DT_DIR)
|
|
|
|
// DTypeFIFO indicates a directory entry is a named pipe (FIFO).
|
|
|
|
DTypeFIFO = DType(C.DT_FIFO)
|
|
|
|
// DTypeLnk indicates a directory entry is a symbolic link.
|
|
|
|
DTypeLnk = DType(C.DT_LNK)
|
|
|
|
// DTypeReg indicates a directory entry is a regular file.
|
|
|
|
DTypeReg = DType(C.DT_REG)
|
|
|
|
// DTypeSock indicates a directory entry is a UNIX domain socket.
|
|
|
|
DTypeSock = DType(C.DT_SOCK)
|
|
|
|
// DTypeUnknown indicates that the file type could not be determined.
|
|
|
|
DTypeUnknown = DType(C.DT_UNKNOWN)
|
|
|
|
)
|
|
|
|
|
2020-02-14 19:50:57 +00:00
|
|
|
// DirEntry represents an entry within a directory.
|
|
|
|
type DirEntry struct {
|
|
|
|
inode Inode
|
|
|
|
name string
|
2020-04-29 14:28:00 +00:00
|
|
|
dtype DType
|
2020-02-14 19:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2020-04-29 14:28:00 +00:00
|
|
|
// DType returns the Directory-entry's Type, indicating if it
|
|
|
|
// is a regular file, directory, etc.
|
|
|
|
// DType may be unknown and thus require an additional call
|
|
|
|
// (stat for example) if Unknown.
|
|
|
|
func (d *DirEntry) DType() DType {
|
|
|
|
return d.dtype
|
|
|
|
}
|
|
|
|
|
2020-05-13 20:36:53 +00:00
|
|
|
// DirEntryPlus is a DirEntry plus additional data (stat) for an entry
|
|
|
|
// within a directory.
|
|
|
|
type DirEntryPlus struct {
|
|
|
|
DirEntry
|
|
|
|
// statx: the converted statx returned by ceph_readdirplus_r
|
|
|
|
statx *CephStatx
|
|
|
|
}
|
|
|
|
|
|
|
|
// Statx returns cached stat metadata for the directory entry.
|
|
|
|
// This call does not incur an actual file system stat.
|
|
|
|
func (d *DirEntryPlus) Statx() *CephStatx {
|
|
|
|
return d.statx
|
|
|
|
}
|
|
|
|
|
2020-02-14 19:50:57 +00:00
|
|
|
// 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]),
|
2020-04-29 14:28:00 +00:00
|
|
|
dtype: DType(de.d_type),
|
2020-02-14 19:50:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-13 20:36:53 +00:00
|
|
|
// toDirEntryPlus converts c structs set by ceph_readdirplus_r to our go
|
|
|
|
// wrapper.
|
|
|
|
func toDirEntryPlus(de *C.struct_dirent, s C.struct_ceph_statx) *DirEntryPlus {
|
|
|
|
return &DirEntryPlus{
|
|
|
|
DirEntry: *toDirEntry(de),
|
|
|
|
statx: cStructToCephStatx(s),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-14 19:50:57 +00:00
|
|
|
// 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
|
|
|
|
}
|
2020-02-14 20:11:06 +00:00
|
|
|
|
2020-05-13 20:36:53 +00:00
|
|
|
// ReadDirPlus reads a single directory entry and stat information from the
|
|
|
|
// open Directory.
|
|
|
|
// A nil DirEntryPlus pointer will be returned when the Directory stream has
|
|
|
|
// been exhausted.
|
|
|
|
// See Statx for a description of the wants and flags parameters.
|
|
|
|
//
|
|
|
|
// Implements:
|
|
|
|
// int ceph_readdirplus_r(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp, struct dirent *de,
|
|
|
|
// struct ceph_statx *stx, unsigned want, unsigned flags, struct Inode **out);
|
|
|
|
func (dir *Directory) ReadDirPlus(
|
|
|
|
want StatxMask, flags AtFlags) (*DirEntryPlus, error) {
|
|
|
|
|
|
|
|
var (
|
|
|
|
de C.struct_dirent
|
|
|
|
s C.struct_ceph_statx
|
|
|
|
)
|
|
|
|
ret := C.ceph_readdirplus_r(
|
|
|
|
dir.mount.mount,
|
|
|
|
dir.dir,
|
|
|
|
&de,
|
|
|
|
&s,
|
|
|
|
C.uint(want),
|
|
|
|
C.uint(flags),
|
|
|
|
nil, // unused, internal Inode type not needed for high level api
|
|
|
|
)
|
|
|
|
if ret < 0 {
|
|
|
|
return nil, getError(ret)
|
|
|
|
}
|
|
|
|
if ret == 0 {
|
|
|
|
return nil, nil // End-of-stream
|
|
|
|
}
|
|
|
|
return toDirEntryPlus(&de, s), nil
|
|
|
|
}
|
|
|
|
|
2020-02-14 20:11:06 +00:00
|
|
|
// RewindDir sets the directory stream to the beginning of the directory.
|
|
|
|
//
|
|
|
|
// Implements:
|
|
|
|
// void ceph_rewinddir(struct ceph_mount_info *cmount, struct ceph_dir_result *dirp);
|
|
|
|
func (dir *Directory) RewindDir() {
|
|
|
|
C.ceph_rewinddir(dir.mount.mount, dir.dir)
|
|
|
|
}
|
|
|
|
|
|
|
|
// dirEntries provides a convenient wrapper around slices of DirEntry items.
|
|
|
|
// For example, use the Names() call to easily get only the names from a
|
|
|
|
// DirEntry slice.
|
|
|
|
type dirEntries []*DirEntry
|
|
|
|
|
|
|
|
// list returns all the contents of a directory as a dirEntries slice.
|
|
|
|
//
|
|
|
|
// list is implemented using ReadDir. If any of the calls to ReadDir returns
|
|
|
|
// an error List will return an error. However, all previous entries
|
|
|
|
// collected will still be returned. Callers of this function may want to check
|
|
|
|
// the entries return value even when an error is returned.
|
|
|
|
// List rewinds the handle every time it is called to get a full
|
|
|
|
// listing of directory contents.
|
|
|
|
func (dir *Directory) list() (dirEntries, error) {
|
|
|
|
var (
|
|
|
|
err error
|
|
|
|
entry *DirEntry
|
|
|
|
entries = make(dirEntries, 0)
|
|
|
|
)
|
|
|
|
dir.RewindDir()
|
|
|
|
for {
|
|
|
|
entry, err = dir.ReadDir()
|
|
|
|
if err != nil || entry == nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
entries = append(entries, entry)
|
|
|
|
}
|
|
|
|
return entries, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// names returns a slice of only the name fields from dir entries.
|
|
|
|
func (entries dirEntries) names() []string {
|
|
|
|
names := make([]string, len(entries))
|
|
|
|
for i, v := range entries {
|
|
|
|
names[i] = v.Name()
|
|
|
|
}
|
|
|
|
return names
|
|
|
|
}
|