filestore.go 6.77 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
	cid "gx/ipfs/QmNp85zy9RLrQ5oQD4hPyS39ezrrXpcaa7R4Y9kxdWQLLQ/go-cid"
Jan Winkelmann's avatar
Jan Winkelmann committed
15
	"gx/ipfs/QmSNbH2A1evCCbJSDC6u3RV3GGDhgu6pRGbXHvrN89tMKf/go-ipfs-cmdkit"
keks's avatar
keks committed
16
	cmds "gx/ipfs/QmUsuV7rMitqBCk2UPmX1f3Vtp4tJNi6xvXpkQgKujjW5R/go-ipfs-cmds"
17 18 19
)

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

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

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

			err = res.Emit(out)
			if err != nil {
				log.Error(err)
			}
73
		} else {
74 75
			fileOrder, _, _ := req.Option("file-order").Bool()
			next, err := filestore.ListAll(fs, fileOrder)
76
			if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
77
				res.SetError(err, cmdkit.ErrNormal)
78 79
				return
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
80 81 82 83 84 85

			out := listResToChan(req.Context(), next)
			err = res.Emit(out)
			if err != nil {
				log.Error(err)
			}
86 87
		}
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
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
	PostRun: cmds.PostRunMap{
		cmds.CLI: func(req cmds.Request, re cmds.ResponseEmitter) cmds.ResponseEmitter {
			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())
					}
114
				}
Jan Winkelmann's avatar
Jan Winkelmann committed
115 116 117 118 119 120 121

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

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

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

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

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

Jan Winkelmann's avatar
Jan Winkelmann committed
239 240 241 242 243 244
type getNoder interface {
	GetNode() (*core.IpfsNode, error)
}

func getFilestore(g getNoder) (*core.IpfsNode, *filestore.Filestore, error) {
	n, err := g.GetNode()
245 246 247 248 249 250 251 252 253 254
	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
255
func listResToChan(ctx context.Context, next func() *filestore.ListRes) <-chan interface{} {
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
	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
}
273

Jan Winkelmann's avatar
Jan Winkelmann committed
274
func perKeyActionToChan(ctx context.Context, args []string, action func(*cid.Cid) *filestore.ListRes) <-chan interface{} {
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
	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
}