func (fs *RedisFS) Lstat(name string) (os.FileInfo, error) {
	return fs.Stat(name)
}
Lstat returns a Inode describing the named file.
RedisFS does not support symbolic links.
Alias for fs.Stat(name)
package redisfs
import (
	"errors"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"github.com/cea-hpc/pdwfs/config"
)
var (
	ErrFileNotManaged = errors.New("File is not managed by pdwfs")
	ErrReadOnly = errors.New("File is read-only")
	ErrWriteOnly = errors.New("File is write-only")
	ErrIsDirectory = errors.New("Is directory")
	ErrNotDirectory = errors.New("Is not a directory")
	ErrDirNotEmpty = errors.New("Directory is not empty")
	ErrParentDirNotExist = errors.New("Parent directory does not exist")
)
type File interface {
	Name() string
	Sync() error
	Truncate(int64) error
	io.Reader
	io.ReaderAt
	io.Writer
	io.WriterAt
	io.Seeker
	io.Closer
	WriteVec([][]byte) (int, error)
	ReadVec([][]byte) (int, error)
	WriteVecAt([][]byte, int64) (int, error)
	ReadVecAt([][]byte, int64) (int, error)
}
const PathSeparator = "/"
type RedisFS struct {
	mountConf *config.Mount
	dataStore *DataStore
	redisRing *RedisRing
	inodes    map[string]*Inode
	root      *Inode
}
func NewRedisFS(redisConf *config.Redis, mountConf *config.Mount) *RedisFS {
	redisRing := NewRedisRing(redisConf)
	dataStore := NewDataStore(redisRing, int64(mountConf.StripeSize))
	root := NewInode(dataStore, redisRing, mountConf.Path)
	root.initMeta(true, 0600)
	return &RedisFS{
		mountConf: mountConf,
		redisRing: redisRing,
		dataStore: dataStore,
		inodes:    map[string]*Inode{root.Path(): root},
		root:      root,
	}
}
func (fs *RedisFS) Finalize() {
	fs.redisRing.Close()
	fs.dataStore.Close()
}
func (fs *RedisFS) ValidatePath(path string) error {
	p, err := filepath.Abs(path)
	if err != nil {
		return err
	}
	if !strings.HasPrefix(p, fs.mountConf.Path) {
		return ErrFileNotManaged
	}
	return nil
}
func (fs *RedisFS) createInode(path string, dir bool, mode os.FileMode, parent *Inode) *Inode {
	i := NewInode(fs.dataStore, fs.redisRing, path)
	i.initMeta(dir, mode)
	parent.setChild(i)
	fs.inodes[i.Path()] = i
	return i
}
func (fs *RedisFS) getInode(path string) (*Inode, bool) {
	if i, ok := fs.inodes[path]; ok {
		return i, true
	}
	i := NewInode(fs.dataStore, fs.redisRing, path)
	if ok := i.exists(); !ok {
		return nil, false
	}
	fs.inodes[i.Path()] = i
	return i, true
}
func (fs *RedisFS) removeInode(i *Inode) {
	i.remove()
	delete(fs.inodes, i.Path())
}
func (fs *RedisFS) fileInfo(abspath string) (parent, node *Inode, err error) {
	if abspath == fs.root.Path() {
		return nil, fs.root, nil
	}
	parentPath := filepath.Dir(abspath)
	fiParent, _ := fs.getInode(parentPath)
	if fiParent == nil || !fiParent.IsDir() {
		return nil, nil, ErrParentDirNotExist
	}
	fiNode, _ := fs.getInode(abspath)
	return fiParent, fiNode, nil
}
func (fs *RedisFS) Mkdir(name string, perm os.FileMode) error {
	if err := fs.ValidatePath(name); err != nil {
		return &os.PathError{Op: "mkdir", Path: name, Err: err}
	}
	path, err := filepath.Abs(name)
	Check(err)
	fiParent, fiNode, err := fs.fileInfo(path)
	if err != nil {
		return &os.PathError{Op: "mkdir", Path: name, Err: err}
	}
	if fiNode == fs.root {
		return nil
	}
	if fiNode != nil {
		return &os.PathError{Op: "mkdir", Path: name, Err: os.ErrExist}
	}
	fs.createInode(path, true, perm, fiParent)
	return nil
}
type byName []os.FileInfo
func (f byName) Len() int { return len(f) }
func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
func (fs *RedisFS) ReadDir(path string) ([]os.FileInfo, error) {
	if err := fs.ValidatePath(path); err != nil {
		return nil, &os.PathError{Op: "readdir", Path: path, Err: err}
	}
	path, err := filepath.Abs(path)
	Check(err)
	_, fi, err := fs.fileInfo(path)
	if err != nil {
		return nil, &os.PathError{Op: "readdir", Path: path, Err: err}
	}
	if fi == nil || !fi.IsDir() {
		return nil, &os.PathError{Op: "readdir", Path: path, Err: ErrNotDirectory}
	}
	fis, err := fi.getChildren()
	if err != nil {
		return nil, &os.PathError{Op: "readdir", Path: path, Err: err}
	}
	f := make([]os.FileInfo, len(fis))
	for i := 0; i < len(fis); i++ {
		f[i] = fis[i]
	}
	sort.Sort(byName(f))
	return f, nil
}
func (fs *RedisFS) RmDir(path string) error {
	if err := fs.ValidatePath(path); err != nil {
		return &os.PathError{Op: "rmdir", Path: path, Err: err}
	}
	path = filepath.Clean(path)
	entries, err := fs.ReadDir(path)
	if err != nil {
		return &os.PathError{Op: "rmdir", Path: path, Err: err}
	}
	if len(entries) != 0 {
		return &os.PathError{Op: "rmdir", Path: path, Err: ErrDirNotEmpty}
	}
	return fs.Remove(path)
}
func hasFlag(flag int, flags int) bool {
	return flags&flag == flag
}
func (fs *RedisFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
	if err := fs.ValidatePath(name); err != nil {
		return nil, &os.PathError{Op: "open", Path: name, Err: err}
	}
	path, err := filepath.Abs(name)
	Check(err)
	fiParent, fiNode, err := fs.fileInfo(path)
	if err != nil {
		return nil, &os.PathError{Op: "open", Path: name, Err: err}
	}
	if fiNode == nil {
		if !hasFlag(os.O_CREATE, flag) {
			return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
		}
		fiNode = fs.createInode(path, false, perm, fiParent)
	} else {  
		if hasFlag(os.O_CREATE|os.O_EXCL, flag) {
			return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrExist}
		}
		if fiNode.IsDir() {
			return nil, &os.PathError{Op: "open", Path: name, Err: ErrIsDirectory}
		}
	}
	return fiNode.getFile(flag)
}
type roFile struct {
	File
}
func (f *roFile) Write(p []byte) (n int, err error) {
	return 0, ErrReadOnly
}
type woFile struct {
	File
}
func (f *woFile) Read(p []byte) (n int, err error) {
	return 0, ErrWriteOnly
}
func (fs *RedisFS) Remove(name string) error {
	if err := fs.ValidatePath(name); err != nil {
		return &os.PathError{Op: "remove", Path: name, Err: err}
	}
	path, err := filepath.Abs(name)
	Check(err)
	fiParent, fiNode, err := fs.fileInfo(path)
	if err != nil {
		return &os.PathError{Op: "remove", Path: name, Err: err}
	}
	if fiNode == nil {
		return &os.PathError{Op: "remove", Path: name, Err: os.ErrNotExist}
	}
	fiParent.removeChild(fiNode)
	fs.removeInode(fiNode)
	return nil
}
func (fs *RedisFS) Stat(name string) (os.FileInfo, error) {
	if err := fs.ValidatePath(name); err != nil {
		return nil, &os.PathError{Op: "stat", Path: name, Err: err}
	}
	path, err := filepath.Abs(name)
	Check(err)
	_, fi, err := fs.fileInfo(path)
	if err != nil {
		return nil, &os.PathError{Op: "stat", Path: name, Err: err}
	}
	if fi == nil {
		return nil, &os.PathError{Op: "stat", Path: name, Err: os.ErrNotExist}
	}
	return fi, nil
}