36533f7c3f
Fixes for new geth version
180 lines
4.6 KiB
Go
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
|
|
}
|