go-billy icon indicating copy to clipboard operation
go-billy copied to clipboard

memfs OpenFile does not work on directories

Open progrium opened this issue 7 years ago • 2 comments

After finally getting a wrapper basically working, I run into an issue because memfs's OpenFile won't open directories. That's not how anything works.

I would strongly encourage you to replace all usage of go-billy with afero, as it fully and properly implements an os "compatible" filesystem abstraction. That said, thank you for your time and effort in this library (though more so with go-git).

progrium avatar Nov 16 '17 05:11 progrium

I found a workaround for the wrapper, but I still think y'all should release yourself from this project.

package main

import (
	"errors"
	"io"
	"os"
	"time"

	"github.com/spf13/afero"
	billy "gopkg.in/src-d/go-billy.v3"
	"gopkg.in/src-d/go-billy.v3/memfs"
)

var ErrNotImplemented = errors.New("not implemented")

type billyToAferoFile struct {
	billy.File

	fs   billy.Filesystem
	path string
}

func (f *billyToAferoFile) WriteAt(p []byte, off int64) (n int, err error) {
	return 0, ErrNotImplemented
}

func (f *billyToAferoFile) Readdir(n int) ([]os.FileInfo, error) {
	// TODO: implement n
	return f.fs.ReadDir(f.path)
}

func (f *billyToAferoFile) Readdirnames(n int) ([]string, error) {
	// TODO: implement n
	files, err := f.fs.ReadDir(f.path)
	if err != nil {
		return nil, err
	}
	var names []string
	for _, info := range files {
		names = append(names, info.Name())
	}
	return names, nil
}

func (f *billyToAferoFile) Stat() (os.FileInfo, error) {
	return f.fs.Stat(f.path)
}

func (f *billyToAferoFile) Sync() error {
	return nil
}

func (f *billyToAferoFile) Truncate(size int64) error {
	return ErrNotImplemented
}

func (f *billyToAferoFile) WriteString(s string) (ret int, err error) {
	return io.WriteString(f, s)
}

type billyToAferoFs struct {
	billy.Filesystem
}

func (fs *billyToAferoFs) Name() string {
	return "billy.Filesystem"
}
func (fs *billyToAferoFs) RemoveAll(path string) error {
	return fs.Remove(path)
}
func (fs *billyToAferoFs) Mkdir(name string, perm os.FileMode) error {
	return fs.MkdirAll(name, perm)
}

func (fs *billyToAferoFs) Chmod(name string, mode os.FileMode) error {
	return nil
}

func (fs *billyToAferoFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
	return nil
}

func (fs *billyToAferoFs) Create(name string) (afero.File, error) {
	f, err := fs.Filesystem.Create(name)
	return &billyToAferoFile{f, fs.Filesystem, name}, err
}

func (fs *billyToAferoFs) Open(name string) (afero.File, error) {
	info, _ := fs.Filesystem.Stat(name)
	if info.IsDir() {
		f, err := memfs.New().Create(name)
		return &billyToAferoFile{f, fs.Filesystem, name}, err
	}
	f, err := fs.Filesystem.Open(name)
	return &billyToAferoFile{f, fs.Filesystem, name}, err
}

func (fs *billyToAferoFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
	info, _ := fs.Filesystem.Stat(name)
	if info.IsDir() {
		f, err := memfs.New().Create(name)
		return &billyToAferoFile{f, fs.Filesystem, name}, err
	}
	f, err := fs.Filesystem.OpenFile(name, flag, perm)
	return &billyToAferoFile{f, fs.Filesystem, name}, err
}

progrium avatar Nov 16 '17 05:11 progrium

@progrium Thanks for sharing the wrapper, that might be useful to others.

Contributions are welcome, so if anyone is willing to work on improving memfs, that will be welcome. I have changed the title of the issue accordingly.

smola avatar Aug 01 '18 14:08 smola