36533f7c3f
Fixes for new geth version
247 lines
5.3 KiB
Go
247 lines
5.3 KiB
Go
package mfs
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
gopath "path"
|
|
"strings"
|
|
|
|
path "github.com/ipfs/go-path"
|
|
|
|
cid "github.com/ipfs/go-cid"
|
|
ipld "github.com/ipfs/go-ipld-format"
|
|
)
|
|
|
|
// TODO: Evaluate moving all this operations to as `Root`
|
|
// methods, since all of them use it as its first argument
|
|
// and there is no clear documentation that explains this
|
|
// separation.
|
|
|
|
// Mv moves the file or directory at 'src' to 'dst'
|
|
// TODO: Document what the strings 'src' and 'dst' represent.
|
|
func Mv(r *Root, src, dst string) error {
|
|
srcDirName, srcFname := gopath.Split(src)
|
|
|
|
var dstDirName string
|
|
var dstFname string
|
|
if dst[len(dst)-1] == '/' {
|
|
dstDirName = dst
|
|
dstFname = srcFname
|
|
} else {
|
|
dstDirName, dstFname = gopath.Split(dst)
|
|
}
|
|
|
|
// get parent directories of both src and dest first
|
|
dstDir, err := lookupDir(r, dstDirName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
srcDir, err := lookupDir(r, srcDirName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
srcObj, err := srcDir.Child(srcFname)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
nd, err := srcObj.GetNode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fsn, err := dstDir.Child(dstFname)
|
|
if err == nil {
|
|
switch n := fsn.(type) {
|
|
case *File:
|
|
_ = dstDir.Unlink(dstFname)
|
|
case *Directory:
|
|
dstDir = n
|
|
dstFname = srcFname
|
|
default:
|
|
return fmt.Errorf("unexpected type at path: %s", dst)
|
|
}
|
|
} else if err != os.ErrNotExist {
|
|
return err
|
|
}
|
|
|
|
err = dstDir.AddChild(dstFname, nd)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if srcDir.name == dstDir.name && srcFname == dstFname {
|
|
return nil
|
|
}
|
|
|
|
return srcDir.Unlink(srcFname)
|
|
}
|
|
|
|
func lookupDir(r *Root, path string) (*Directory, error) {
|
|
di, err := Lookup(r, path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
d, ok := di.(*Directory)
|
|
if !ok {
|
|
return nil, fmt.Errorf("%s is not a directory", path)
|
|
}
|
|
|
|
return d, nil
|
|
}
|
|
|
|
// PutNode inserts 'nd' at 'path' in the given mfs
|
|
// TODO: Rename or clearly document that this is not about nodes but actually
|
|
// MFS files/directories (that in the underlying representation can be
|
|
// considered as just nodes).
|
|
// TODO: Document why are we handling IPLD nodes in the first place when we
|
|
// are actually referring to files/directories (that is, it can't be any
|
|
// node, it has to have a specific format).
|
|
// TODO: Can this function add directories or just files? What would be the
|
|
// difference between adding a directory with this method and creating it
|
|
// with `Mkdir`.
|
|
func PutNode(r *Root, path string, nd ipld.Node) error {
|
|
dirp, filename := gopath.Split(path)
|
|
if filename == "" {
|
|
return fmt.Errorf("cannot create file with empty name")
|
|
}
|
|
|
|
pdir, err := lookupDir(r, dirp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return pdir.AddChild(filename, nd)
|
|
}
|
|
|
|
// MkdirOpts is used by Mkdir
|
|
type MkdirOpts struct {
|
|
Mkparents bool
|
|
Flush bool
|
|
CidBuilder cid.Builder
|
|
}
|
|
|
|
// Mkdir creates a directory at 'path' under the directory 'd', creating
|
|
// intermediary directories as needed if 'mkparents' is set to true
|
|
func Mkdir(r *Root, pth string, opts MkdirOpts) error {
|
|
if pth == "" {
|
|
return fmt.Errorf("no path given to Mkdir")
|
|
}
|
|
parts := path.SplitList(pth)
|
|
if parts[0] == "" {
|
|
parts = parts[1:]
|
|
}
|
|
|
|
// allow 'mkdir /a/b/c/' to create c
|
|
if parts[len(parts)-1] == "" {
|
|
parts = parts[:len(parts)-1]
|
|
}
|
|
|
|
if len(parts) == 0 {
|
|
// this will only happen on 'mkdir /'
|
|
if opts.Mkparents {
|
|
return nil
|
|
}
|
|
return fmt.Errorf("cannot create directory '/': Already exists")
|
|
}
|
|
|
|
cur := r.GetDirectory()
|
|
for i, d := range parts[:len(parts)-1] {
|
|
fsn, err := cur.Child(d)
|
|
if err == os.ErrNotExist && opts.Mkparents {
|
|
mkd, err := cur.Mkdir(d)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if opts.CidBuilder != nil {
|
|
mkd.SetCidBuilder(opts.CidBuilder)
|
|
}
|
|
fsn = mkd
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
next, ok := fsn.(*Directory)
|
|
if !ok {
|
|
return fmt.Errorf("%s was not a directory", path.Join(parts[:i]))
|
|
}
|
|
cur = next
|
|
}
|
|
|
|
final, err := cur.Mkdir(parts[len(parts)-1])
|
|
if err != nil {
|
|
if !opts.Mkparents || err != os.ErrExist || final == nil {
|
|
return err
|
|
}
|
|
}
|
|
if opts.CidBuilder != nil {
|
|
final.SetCidBuilder(opts.CidBuilder)
|
|
}
|
|
|
|
if opts.Flush {
|
|
err := final.Flush()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Lookup extracts the root directory and performs a lookup under it.
|
|
// TODO: Now that the root is always a directory, can this function
|
|
// be collapsed with `DirLookup`? Or at least be made a method of `Root`?
|
|
func Lookup(r *Root, path string) (FSNode, error) {
|
|
dir := r.GetDirectory()
|
|
|
|
return DirLookup(dir, path)
|
|
}
|
|
|
|
// DirLookup will look up a file or directory at the given path
|
|
// under the directory 'd'
|
|
func DirLookup(d *Directory, pth string) (FSNode, error) {
|
|
pth = strings.Trim(pth, "/")
|
|
parts := path.SplitList(pth)
|
|
if len(parts) == 1 && parts[0] == "" {
|
|
return d, nil
|
|
}
|
|
|
|
var cur FSNode
|
|
cur = d
|
|
for i, p := range parts {
|
|
chdir, ok := cur.(*Directory)
|
|
if !ok {
|
|
return nil, fmt.Errorf("cannot access %s: Not a directory", path.Join(parts[:i+1]))
|
|
}
|
|
|
|
child, err := chdir.Child(p)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cur = child
|
|
}
|
|
return cur, nil
|
|
}
|
|
|
|
// TODO: Document this function and link its functionality
|
|
// with the republisher.
|
|
func FlushPath(ctx context.Context, rt *Root, pth string) (ipld.Node, error) {
|
|
nd, err := Lookup(rt, pth)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = nd.Flush()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
rt.repub.WaitPub(ctx)
|
|
return nd.GetNode()
|
|
}
|