You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
3.0 KiB
Go

package storage
import (
"errors"
"io"
"log"
"strings"
"time"
ipfsapi "github.com/ipfs/go-ipfs-api"
"golang.org/x/net/context"
)
func NewIPFSFilestore(ipfs *ipfsapi.Shell, rootDir string) (*IPFSFilestore, error) {
store := &IPFSFilestore{
ipfs: ipfs,
rootDir: rootDir,
}
exists, err := store.Exists("")
if err != nil {
return nil, err
}
if !exists {
err := ipfs.FilesMkdir(newContext(), rootDir)
if err != nil {
return nil, err
}
}
return store, nil
}
type IPFSFilestore struct {
ipfs *ipfsapi.Shell
rootDir string
}
var ErrFileNotFound = errors.New("File not found")
type ErrFailedFileExistsCheck struct {
parent error
}
func (e *ErrFailedFileExistsCheck) Unwrap() error {
return e.parent
}
func (e *ErrFailedFileExistsCheck) Error() string {
msg := "Failed to determine whether file exists"
if e.parent != nil {
msg += ": " + e.parent.Error()
}
return msg
}
func newContext() context.Context {
ctx, _ := context.WithTimeout(context.Background(), 10*time.Minute)
return ctx
}
func (s *IPFSFilestore) Exists(name string) (bool, error) {
ctx := newContext()
_, err := s.ipfs.FilesStat(ctx, s.filepath(name))
if err != nil {
if err.Error() == "files/stat: file does not exist" {
return false, nil
}
return false, &ErrFailedFileExistsCheck{parent: err}
}
return true, nil
}
func (s *IPFSFilestore) DirectoryExists(name string) (bool, error) {
ctx := newContext()
st, err := s.ipfs.FilesStat(ctx, s.filepath(name))
if err != nil {
if err.Error() == "files/stat: file does not exist" {
return false, nil
}
return false, &ErrFailedFileExistsCheck{parent: err}
}
if st.Type != "directory" {
return false, nil
}
return true, nil
}
func (s *IPFSFilestore) filepath(name string) string {
return s.rootDir + "/" + strings.TrimPrefix(name, "/")
}
func (s *IPFSFilestore) MakeDirectory(name string) error {
filepath := s.filepath(name)
log.Printf("Making directory \"%s\"", filepath)
return s.ipfs.FilesMkdir(newContext(), filepath)
}
type FileReader struct {
ipfs *ipfsapi.Shell
file *File
reader io.ReadCloser
}
func (r *FileReader) Read(p []byte) (int, error) {
if r.reader == nil {
var err error
r.reader, err = r.ipfs.FilesRead(newContext(), r.file.AbsolutePath)
if err != nil {
return 0, err
}
}
return r.reader.Read(p)
}
func (r *FileReader) Close() error {
if r.reader == nil {
return nil
}
return r.reader.Close()
}
func (s *IPFSFilestore) Get(name string) (*File, error) {
exists, err := s.Exists(name)
if err != nil {
return nil, err
}
if !exists {
return nil, ErrFileNotFound
}
file := &File{
Name: name,
AbsolutePath: s.filepath(name),
}
file.Contents = &FileReader{
ipfs: s.ipfs,
file: file,
}
return file, nil
}
func (s *IPFSFilestore) Set(file *File) error {
return s.ipfs.FilesWrite(newContext(), s.filepath(file.Name), file.Contents)
}
func (s *IPFSFilestore) Delete(file *File) error {
return s.ipfs.FilesRm(newContext(), file.AbsolutePath, true)
}