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

import (
	"context"
	"fmt"
6
	"io"
Jan Winkelmann's avatar
Jan Winkelmann committed
7
	"os"
8

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

14
	lgc "github.com/ipfs/go-ipfs/commands/legacy"
Steven Allen's avatar
Steven Allen committed
15
	cid "gx/ipfs/QmcZfnkapfECQGcLZaf9B79NRg7cRa9EnZh4LSbkCzwNvY/go-cid"
16
	"gx/ipfs/QmceUdzxkimdYsgtX733uNgzf1DLHyBKN6ehGSp85ayppM/go-ipfs-cmdkit"
17
	cmds "gx/ipfs/QmfAkMSt9Fwzk48QDJecPcwCUjnf2uG7MLnmCGTp4C6ouL/go-ipfs-cmds"
18 19 20
)

var FileStoreCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
21
	Helptext: cmdkit.HelpText{
22 23 24
		Tagline: "Interact with filestore objects.",
	},
	Subcommands: map[string]*cmds.Command{
25 26 27
		"ls":     lsFileStore,
		"verify": lgc.NewCommand(verifyFileStore),
		"dups":   lgc.NewCommand(dupsFileStore),
28 29 30
	},
}

Jan Winkelmann's avatar
Jan Winkelmann committed
31 32 33 34 35
type lsEncoder struct {
	errors bool
	w      io.Writer
}

36
var lsFileStore = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
37
	Helptext: cmdkit.HelpText{
38
		Tagline: "List objects in filestore.",
39 40 41 42 43 44 45 46 47 48
		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>
`,
49
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
50 51
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("obj", false, true, "Cid of objects to list."),
52
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
53 54
	Options: []cmdkit.Option{
		cmdkit.BoolOption("file-order", "sort the results based on the path of the backing file"),
55
	},
Jeromy's avatar
Jeromy committed
56
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) {
Steven Allen's avatar
Steven Allen committed
57
		_, fs, err := getFilestore(env)
58
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
59
			res.SetError(err, cmdkit.ErrNormal)
60 61
			return
		}
62
		args := req.Arguments
63
		if len(args) > 0 {
64
			out := perKeyActionToChan(req.Context, args, func(c *cid.Cid) *filestore.ListRes {
65
				return filestore.List(fs, c)
Jan Winkelmann's avatar
Jan Winkelmann committed
66 67 68 69 70 71
			})

			err = res.Emit(out)
			if err != nil {
				log.Error(err)
			}
72
		} else {
73
			fileOrder, _ := req.Options["file-order"].(bool)
74
			next, err := filestore.ListAll(fs, fileOrder)
75
			if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
76
				res.SetError(err, cmdkit.ErrNormal)
77 78
				return
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
79

80
			out := listResToChan(req.Context, next)
Jan Winkelmann's avatar
Jan Winkelmann committed
81 82 83 84
			err = res.Emit(out)
			if err != nil {
				log.Error(err)
			}
85 86
		}
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
87
	PostRun: cmds.PostRunMap{
88
		cmds.CLI: func(req *cmds.Request, re cmds.ResponseEmitter) cmds.ResponseEmitter {
Jan Winkelmann's avatar
Jan Winkelmann committed
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
			reNext, res := cmds.NewChanResponsePair(req)

			go func() {
				defer re.Close()

				var errors bool
				for {
					v, err := res.Next()
					if !cmds.HandleError(err, res, re) {
						break
					}

					r, ok := v.(*filestore.ListRes)
					if !ok {
						log.Error(e.New(e.TypeErr(r, v)))
						return
					}

					if r.ErrorMsg != "" {
						errors = true
						fmt.Fprintf(os.Stderr, "%s\n", r.ErrorMsg)
					} else {
						fmt.Fprintf(os.Stdout, "%s\n", r.FormatLong())
					}
113
				}
Jan Winkelmann's avatar
Jan Winkelmann committed
114 115 116 117 118 119 120

				if errors {
					re.SetError("errors while displaying some entries", cmdkit.ErrNormal)
				}
			}()

			return reNext
121
		},
122 123 124 125
	},
	Type: filestore.ListRes{},
}

Jan Winkelmann's avatar
Jan Winkelmann committed
126 127
var verifyFileStore = &oldCmds.Command{
	Helptext: cmdkit.HelpText{
128
		Tagline: "Verify objects in filestore.",
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
		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.
`,
149
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
150 151
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("obj", false, true, "Cid of objects to verify."),
152
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
153 154
	Options: []cmdkit.Option{
		cmdkit.BoolOption("file-order", "verify the objects based on the order of the backing file"),
155
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
156 157
	Run: func(req oldCmds.Request, res oldCmds.Response) {
		_, fs, err := getFilestore(req.InvocContext())
158
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
159
			res.SetError(err, cmdkit.ErrNormal)
160 161
			return
		}
162 163
		args := req.Arguments()
		if len(args) > 0 {
Jan Winkelmann's avatar
Jan Winkelmann committed
164
			out := perKeyActionToChan(req.Context(), args, func(c *cid.Cid) *filestore.ListRes {
165
				return filestore.Verify(fs, c)
Jan Winkelmann's avatar
Jan Winkelmann committed
166
			})
167 168
			res.SetOutput(out)
		} else {
169 170
			fileOrder, _, _ := req.Option("file-order").Bool()
			next, err := filestore.VerifyAll(fs, fileOrder)
171
			if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
172
				res.SetError(err, cmdkit.ErrNormal)
173 174
				return
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
175
			out := listResToChan(req.Context(), next)
176
			res.SetOutput(out)
177 178
		}
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
179 180 181 182 183 184 185 186
	Marshalers: oldCmds.MarshalerMap{
		oldCmds.Text: func(res oldCmds.Response) (io.Reader, error) {
			v, err := unwrapOutput(res.Output())
			if err != nil {
				return nil, err
			}

			r, ok := v.(*filestore.ListRes)
187
			if !ok {
Jan Winkelmann's avatar
Jan Winkelmann committed
188
				return nil, e.TypeErr(r, v)
189
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
190 191 192

			if r.Status == filestore.StatusOtherError {
				fmt.Fprintf(res.Stderr(), "%s\n", r.ErrorMsg)
193
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
194
			fmt.Fprintf(res.Stdout(), "%s %s\n", r.Status.Format(), r.FormatLong())
195 196
			return nil, nil
		},
197 198 199 200
	},
	Type: filestore.ListRes{},
}

Jan Winkelmann's avatar
Jan Winkelmann committed
201 202
var dupsFileStore = &oldCmds.Command{
	Helptext: cmdkit.HelpText{
203
		Tagline: "List blocks that are both in the filestore and standard block storage.",
204
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
205 206
	Run: func(req oldCmds.Request, res oldCmds.Response) {
		_, fs, err := getFilestore(req.InvocContext())
207
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
208
			res.SetError(err, cmdkit.ErrNormal)
209 210 211 212
			return
		}
		ch, err := fs.FileManager().AllKeysChan(req.Context())
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
213
			res.SetError(err, cmdkit.ErrNormal)
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
			return
		}

		out := make(chan interface{}, 128)
		res.SetOutput((<-chan interface{})(out))

		go func() {
			defer close(out)
			for cid := range ch {
				have, err := fs.MainBlockstore().Has(cid)
				if err != nil {
					out <- &RefWrapper{Err: err.Error()}
					return
				}
				if have {
					out <- &RefWrapper{Ref: cid.String()}
				}
			}
		}()
	},
	Marshalers: refsMarshallerMap,
	Type:       RefWrapper{},
}

238 239
func getFilestore(env interface{}) (*core.IpfsNode, *filestore.Filestore, error) {
	n, err := GetNode(env)
240 241 242 243 244 245 246 247 248 249
	if err != nil {
		return nil, nil, err
	}
	fs := n.Filestore
	if fs == nil {
		return n, nil, fmt.Errorf("filestore not enabled")
	}
	return n, fs, err
}

Jan Winkelmann's avatar
Jan Winkelmann committed
250
func listResToChan(ctx context.Context, next func() *filestore.ListRes) <-chan interface{} {
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	out := make(chan interface{}, 128)
	go func() {
		defer close(out)
		for {
			r := next()
			if r == nil {
				return
			}
			select {
			case out <- r:
			case <-ctx.Done():
				return
			}
		}
	}()
	return out
}
268

Jan Winkelmann's avatar
Jan Winkelmann committed
269
func perKeyActionToChan(ctx context.Context, args []string, action func(*cid.Cid) *filestore.ListRes) <-chan interface{} {
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
	out := make(chan interface{}, 128)
	go func() {
		defer close(out)
		for _, arg := range args {
			c, err := cid.Decode(arg)
			if err != nil {
				out <- &filestore.ListRes{
					Status:   filestore.StatusOtherError,
					ErrorMsg: fmt.Sprintf("%s: %v", arg, err),
				}
				continue
			}
			r := action(c)
			select {
			case out <- r:
			case <-ctx.Done():
				return
			}
		}
	}()
	return out
}