filestore.go 5.65 KB
Newer Older
1 2 3 4
package commands

import (
	"fmt"
5
	"io"
6
	"os"
7

8
	filestore "github.com/ipfs/go-filestore"
9
	core "github.com/ipfs/go-ipfs/core"
10
	cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
11
	e "github.com/ipfs/go-ipfs/core/commands/e"
Jan Winkelmann's avatar
Jan Winkelmann committed
12

Steven Allen's avatar
Steven Allen committed
13 14
	"github.com/ipfs/go-cid"
	"github.com/ipfs/go-ipfs-cmds"
15 16 17
)

var FileStoreCmd = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
18
	Helptext: cmds.HelpText{
19 20 21
		Tagline: "Interact with filestore objects.",
	},
	Subcommands: map[string]*cmds.Command{
22
		"ls":     lsFileStore,
23 24
		"verify": verifyFileStore,
		"dups":   dupsFileStore,
25 26 27
	},
}

Kejie Zhang's avatar
Kejie Zhang committed
28 29 30 31
const (
	fileOrderOptionName = "file-order"
)

32
var lsFileStore = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
33
	Helptext: cmds.HelpText{
34
		Tagline: "List objects in filestore.",
35 36 37 38 39 40 41 42 43 44
		LongDescription: `
List objects in the filestore.

If one or more <obj> is specified only list those specific objects,
otherwise list all objects.

The output is:

<hash> <size> <path> <offset>
`,
45
	},
Steven Allen's avatar
Steven Allen committed
46 47
	Arguments: []cmds.Argument{
		cmds.StringArg("obj", false, true, "Cid of objects to list."),
48
	},
Steven Allen's avatar
Steven Allen committed
49 50
	Options: []cmds.Option{
		cmds.BoolOption(fileOrderOptionName, "sort the results based on the path of the backing file"),
51
	},
keks's avatar
keks committed
52
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
Steven Allen's avatar
Steven Allen committed
53
		_, fs, err := getFilestore(env)
54
		if err != nil {
keks's avatar
keks committed
55
			return err
56
		}
57
		args := req.Arguments
58
		if len(args) > 0 {
59
			return listByArgs(res, fs, args)
keks's avatar
keks committed
60
		}
Jan Winkelmann's avatar
Jan Winkelmann committed
61

Kejie Zhang's avatar
Kejie Zhang committed
62
		fileOrder, _ := req.Options[fileOrderOptionName].(bool)
keks's avatar
keks committed
63 64 65
		next, err := filestore.ListAll(fs, fileOrder)
		if err != nil {
			return err
66
		}
keks's avatar
keks committed
67

68 69 70 71 72 73 74 75 76 77 78
		for {
			r := next()
			if r == nil {
				break
			}
			if err := res.Emit(r); err != nil {
				return err
			}
		}

		return nil
79
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
80
	PostRun: cmds.PostRunMap{
81 82 83 84
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			enc, err := cmdenv.GetCidEncoder(res.Request())
			if err != nil {
				return err
keks's avatar
keks committed
85
			}
86 87 88 89 90 91 92 93 94
			return streamResult(func(v interface{}, out io.Writer) nonFatalError {
				r := v.(*filestore.ListRes)
				if r.ErrorMsg != "" {
					return nonFatalError(r.ErrorMsg)
				}
				fmt.Fprintf(out, "%s\n", r.FormatLong(enc.Encode))
				return ""
			})(res, re)
		},
95 96 97 98
	},
	Type: filestore.ListRes{},
}

99
var verifyFileStore = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
100
	Helptext: cmds.HelpText{
101
		Tagline: "Verify objects in filestore.",
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
		LongDescription: `
Verify objects in the filestore.

If one or more <obj> is specified only verify those specific objects,
otherwise verify all objects.

The output is:

<status> <hash> <size> <path> <offset>

Where <status> is one of:
ok:       the block can be reconstructed
changed:  the contents of the backing file have changed
no-file:  the backing file could not be found
error:    there was some other problem reading the file
missing:  <obj> could not be found in the filestore
ERROR:    internal error, most likely due to a corrupt database

For ERROR entries the error will also be printed to stderr.
`,
122
	},
Steven Allen's avatar
Steven Allen committed
123 124
	Arguments: []cmds.Argument{
		cmds.StringArg("obj", false, true, "Cid of objects to verify."),
125
	},
Steven Allen's avatar
Steven Allen committed
126 127
	Options: []cmds.Option{
		cmds.BoolOption(fileOrderOptionName, "verify the objects based on the order of the backing file"),
128
	},
129 130
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		_, fs, err := getFilestore(env)
131
		if err != nil {
132
			return err
133
		}
134
		args := req.Arguments
135
		if len(args) > 0 {
136
			return listByArgs(res, fs, args)
137
		}
Overbool's avatar
Overbool committed
138 139 140 141 142 143

		fileOrder, _ := req.Options[fileOrderOptionName].(bool)
		next, err := filestore.VerifyAll(fs, fileOrder)
		if err != nil {
			return err
		}
144 145 146 147 148 149 150 151 152 153 154 155

		for {
			r := next()
			if r == nil {
				break
			}
			if err := res.Emit(r); err != nil {
				return err
			}
		}

		return nil
156
	},
157 158
	PostRun: cmds.PostRunMap{
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
159 160 161 162 163
			enc, err := cmdenv.GetCidEncoder(res.Request())
			if err != nil {
				return err
			}

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
			for {
				v, err := res.Next()
				if err != nil {
					if err == io.EOF {
						return nil
					}
					return err
				}

				list, ok := v.(*filestore.ListRes)
				if !ok {
					return e.TypeErr(list, v)
				}

				if list.Status == filestore.StatusOtherError {
					fmt.Fprintf(os.Stderr, "%s\n", list.ErrorMsg)
				}
181
				fmt.Fprintf(os.Stdout, "%s %s\n", list.Status.Format(), list.FormatLong(enc.Encode))
182
			}
183
		},
184 185 186 187
	},
	Type: filestore.ListRes{},
}

188
var dupsFileStore = &cmds.Command{
Steven Allen's avatar
Steven Allen committed
189
	Helptext: cmds.HelpText{
190
		Tagline: "List blocks that are both in the filestore and standard block storage.",
191
	},
192 193
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
		_, fs, err := getFilestore(env)
194
		if err != nil {
195
			return err
196
		}
197 198 199 200 201 202

		enc, err := cmdenv.GetCidEncoder(req)
		if err != nil {
			return err
		}

203
		ch, err := fs.FileManager().AllKeysChan(req.Context)
204
		if err != nil {
205
			return err
206 207
		}

208 209 210 211 212 213
		for cid := range ch {
			have, err := fs.MainBlockstore().Has(cid)
			if err != nil {
				return res.Emit(&RefWrapper{Err: err.Error()})
			}
			if have {
214
				if err := res.Emit(&RefWrapper{Ref: enc.Encode(cid)}); err != nil {
215
					return err
216 217
				}
			}
218 219 220
		}

		return nil
221
	},
Overbool's avatar
Overbool committed
222 223
	Encoders: refsEncoderMap,
	Type:     RefWrapper{},
224 225
}

226
func getFilestore(env cmds.Environment) (*core.IpfsNode, *filestore.Filestore, error) {
227
	n, err := cmdenv.GetNode(env)
228 229 230 231 232
	if err != nil {
		return nil, nil, err
	}
	fs := n.Filestore
	if fs == nil {
233
		return n, nil, filestore.ErrFilestoreNotEnabled
234 235 236 237
	}
	return n, fs, err
}

238 239 240 241 242 243 244
func listByArgs(res cmds.ResponseEmitter, fs *filestore.Filestore, args []string) error {
	for _, arg := range args {
		c, err := cid.Decode(arg)
		if err != nil {
			ret := &filestore.ListRes{
				Status:   filestore.StatusOtherError,
				ErrorMsg: fmt.Sprintf("%s: %v", arg, err),
245
			}
246 247
			if err := res.Emit(ret); err != nil {
				return err
248
			}
249
			continue
250
		}
251 252 253
		r := filestore.Verify(fs, c)
		if err := res.Emit(r); err != nil {
			return err
254
		}
255 256 257
	}

	return nil
258
}