convert.go 3.33 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
// Package flatfs is a Datastore implementation that stores all
// objects in a two-level directory structure in the local file
// system, regardless of the hierarchy of the keys.
package flatfs

import (
	"fmt"
	"io"
	"os"
	"path/filepath"
	"time"

	"github.com/ipfs/go-datastore"
	"github.com/ipfs/go-datastore/query"
	"github.com/jbenet/go-os-rename"
)

func UpgradeV0toV1(path string, prefixLen int) error {
	fun := Prefix(prefixLen)
	err := WriteShardFunc(path, fun)
	if err != nil {
		return err
	}
	err = WriteReadme(path, fun)
	if err != nil {
		return err
	}
	return nil
}

func DowngradeV1toV0(path string) error {
	err := os.Remove(filepath.Join(path, SHARDING_FN))
	if err != nil {
		return err
	}
	err = os.Remove(filepath.Join(path, README_FN))
	if err != nil && !os.IsNotExist(err) {
		return err
	}
	return nil
}

func Move(oldPath string, newPath string, out io.Writer) error {
	oldDS, err := Open(oldPath, false)
	if err != nil {
		return fmt.Errorf("%s: %v", oldPath, err)
	}
	newDS, err := Open(newPath, false)
	if err != nil {
		return fmt.Errorf("%s: %v", newPath, err)
	}

	if out != nil {
		fmt.Fprintf(out, "Getting Keys...\n")
	}
	res, err := oldDS.Query(query.Query{KeysOnly: true})
	if err != nil {
		return err
	}
	entries, err := res.Rest()
	if err != nil {
		return err
	}
	prog := newProgress(len(entries), out)

	if out != nil {
		fmt.Fprintf(out, "Moving Keys...\n")
	}

	// first move the keys
	for _, e := range entries {
		err := moveKey(oldDS, newDS, datastore.RawKey(e.Key))
		if err != nil {
			return err
		}
		prog.Next()
	}

	if out != nil {
		fmt.Fprintf(out, "\nCleaning Up...\n")
	}

	// now walk the old top-level directory
	dir, err := os.Open(oldDS.path)
	if err != nil {
		return err
	}
	defer dir.Close()
	names, err := dir.Readdirnames(-1)
	if err != nil {
		return err
	}
	for _, fn := range names {
		if fn == "." || fn == ".." {
			continue
		}
		oldPath := filepath.Join(oldDS.path, fn)
		inf, err := os.Stat(oldPath)
		if err != nil {
			return err
		}
		if inf.IsDir() || fn == "SHARDING" || fn == "_README" {
			// if we are a director or generated file just remove it
			err := os.Remove(oldPath)
			if err != nil {
				return err
			}
		} else {
			// else move it
			newPath := filepath.Join(newDS.path, fn)
			err := osrename.Rename(oldPath, newPath)
			if err != nil {
				return err
			}
		}
	}

	if out != nil {
		fmt.Fprintf(out, "All Done.\n")
	}

	return nil
}

func moveKey(oldDS *Datastore, newDS *Datastore, key datastore.Key) error {
	_, oldPath := oldDS.encode(key)
	dir, newPath := newDS.encode(key)
	err := newDS.makeDirNoSync(dir)
	if err != nil {
		return err
	}
	err = osrename.Rename(oldPath, newPath)
	if err != nil {
		return err
	}
	return nil
}

type progress struct {
	total   int
	current int

	out io.Writer

	start time.Time
}

func newProgress(total int, out io.Writer) *progress {
	return &progress{
		total: total,
		start: time.Now(),
		out:   out,
	}
}

func (p *progress) Next() {
	p.current++
	if p.out == nil {
		return
	}
	if p.current%10 == 0 || p.current == p.total {
		fmt.Fprintf(p.out, "\r[%d / %d]", p.current, p.total)
	}

	if p.current%100 == 0 || p.current == p.total {
		took := time.Now().Sub(p.start)
		av := took / time.Duration(p.current)
		estim := av * time.Duration(p.total-p.current)
		//est := strings.Split(estim.String(), ".")[0]

		fmt.Fprintf(p.out, "  Approx time remaining: %s  ", estim)
	}
}