ipld-eth-server/vendor/github.com/ipfs/go-mfs/file.go
Elizabeth Engelman 36533f7c3f Update vendor directory and make necessary code changes
Fixes for new geth version
2019-09-25 16:32:27 -05:00

180 lines
4.6 KiB
Go

package mfs
import (
"context"
"fmt"
"sync"
dag "github.com/ipfs/go-merkledag"
ft "github.com/ipfs/go-unixfs"
mod "github.com/ipfs/go-unixfs/mod"
chunker "github.com/ipfs/go-ipfs-chunker"
ipld "github.com/ipfs/go-ipld-format"
)
// File represents a file in the MFS, its logic its mainly targeted
// to coordinating (potentially many) `FileDescriptor`s pointing to
// it.
type File struct {
inode
// Lock to coordinate the `FileDescriptor`s associated to this file.
desclock sync.RWMutex
// This isn't any node, it's the root node that represents the
// entire DAG of nodes that comprise the file.
// TODO: Rename, there should be an explicit term for these root nodes
// of a particular sub-DAG that abstract an upper layer's entity.
node ipld.Node
// Lock around the `node` that represents this file, necessary because
// there may be many `FileDescriptor`s operating on this `File`.
nodeLock sync.RWMutex
RawLeaves bool
}
// NewFile returns a NewFile object with the given parameters. If the
// Cid version is non-zero RawLeaves will be enabled.
func NewFile(name string, node ipld.Node, parent parent, dserv ipld.DAGService) (*File, error) {
fi := &File{
inode: inode{
name: name,
parent: parent,
dagService: dserv,
},
node: node,
}
if node.Cid().Prefix().Version > 0 {
fi.RawLeaves = true
}
return fi, nil
}
func (fi *File) Open(flags Flags) (_ FileDescriptor, _retErr error) {
if flags.Write {
fi.desclock.Lock()
defer func() {
if _retErr != nil {
fi.desclock.Unlock()
}
}()
} else if flags.Read {
fi.desclock.RLock()
defer func() {
if _retErr != nil {
fi.desclock.Unlock()
}
}()
} else {
return nil, fmt.Errorf("file opened for neither reading nor writing")
}
fi.nodeLock.RLock()
node := fi.node
fi.nodeLock.RUnlock()
// TODO: Move this `switch` logic outside (maybe even
// to another package, this seems like a job of UnixFS),
// `NewDagModifier` uses the IPLD node, we're not
// extracting anything just doing a safety check.
switch node := node.(type) {
case *dag.ProtoNode:
fsn, err := ft.FSNodeFromBytes(node.Data())
if err != nil {
return nil, err
}
switch fsn.Type() {
default:
return nil, fmt.Errorf("unsupported fsnode type for 'file'")
case ft.TSymlink:
return nil, fmt.Errorf("symlinks not yet supported")
case ft.TFile, ft.TRaw:
// OK case
}
case *dag.RawNode:
// Ok as well.
}
dmod, err := mod.NewDagModifier(context.TODO(), node, fi.dagService, chunker.DefaultSplitter)
// TODO: Remove the use of the `chunker` package here, add a new `NewDagModifier` in
// `go-unixfs` with the `DefaultSplitter` already included.
if err != nil {
return nil, err
}
dmod.RawLeaves = fi.RawLeaves
return &fileDescriptor{
inode: fi,
flags: flags,
mod: dmod,
state: stateCreated,
}, nil
}
// Size returns the size of this file
// TODO: Should we be providing this API?
// TODO: There's already a `FileDescriptor.Size()` that
// through the `DagModifier`'s `fileSize` function is doing
// pretty much the same thing as here, we should at least call
// that function and wrap the `ErrNotUnixfs` with an MFS text.
func (fi *File) Size() (int64, error) {
fi.nodeLock.RLock()
defer fi.nodeLock.RUnlock()
switch nd := fi.node.(type) {
case *dag.ProtoNode:
fsn, err := ft.FSNodeFromBytes(nd.Data())
if err != nil {
return 0, err
}
return int64(fsn.FileSize()), nil
case *dag.RawNode:
return int64(len(nd.RawData())), nil
default:
return 0, fmt.Errorf("unrecognized node type in mfs/file.Size()")
}
}
// GetNode returns the dag node associated with this file
// TODO: Use this method and do not access the `nodeLock` directly anywhere else.
func (fi *File) GetNode() (ipld.Node, error) {
fi.nodeLock.RLock()
defer fi.nodeLock.RUnlock()
return fi.node, nil
}
// TODO: Tight coupling with the `FileDescriptor`, at the
// very least this should be an independent function that
// takes a `File` argument and automates the open/flush/close
// operations.
// TODO: Why do we need to flush a file that isn't opened?
// (the `OpenWriteOnly` seems to implicitly be targeting a
// closed file, a file we forgot to flush? can we close
// a file without flushing?)
func (fi *File) Flush() error {
// open the file in fullsync mode
fd, err := fi.Open(Flags{Write: true, Sync: true})
if err != nil {
return err
}
defer fd.Close()
return fd.Flush()
}
func (fi *File) Sync() error {
// just being able to take the writelock means the descriptor is synced
// TODO: Why?
fi.desclock.Lock()
fi.desclock.Unlock()
return nil
}
// Type returns the type FSNode this is
func (fi *File) Type() NodeType {
return TFile
}