filestore.go 6.63 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"
Łukasz Magiera's avatar
Łukasz Magiera committed
10
	lgc "github.com/ipfs/go-ipfs/commands/legacy"
11
	"github.com/ipfs/go-ipfs/core"
12
	cmdenv "github.com/ipfs/go-ipfs/core/commands/cmdenv"
Jan Winkelmann's avatar
Jan Winkelmann committed
13
	e "github.com/ipfs/go-ipfs/core/commands/e"
14
	"github.com/ipfs/go-ipfs/filestore"
Jan Winkelmann's avatar
Jan Winkelmann committed
15

16
	cid "gx/ipfs/QmPSQnBKM9g7BaUcZCvswUJVscQ1ipjmwxN5PXCjkp9EQ7/go-cid"
17
	"gx/ipfs/QmSP88ryZkHSRn1fnngAaV2Vcn63WUJzAavnRM9CVdU1Ky/go-ipfs-cmdkit"
Steven Allen's avatar
Steven Allen committed
18
	cmds "gx/ipfs/QmXTmUCBtDUrzDYVzASogLiNph7EBuYqEgPL7QoHNMzUnz/go-ipfs-cmds"
19 20 21
)

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

var lsFileStore = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
33
	Helptext: cmdkit.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
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
46 47
	Arguments: []cmdkit.Argument{
		cmdkit.StringArg("obj", false, true, "Cid of objects to list."),
48
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
49 50
	Options: []cmdkit.Option{
		cmdkit.BoolOption("file-order", "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
			out := perKeyActionToChan(req.Context, args, func(c cid.Cid) *filestore.ListRes {
60
				return filestore.List(fs, c)
Jan Winkelmann's avatar
Jan Winkelmann committed
61 62
			})

keks's avatar
keks committed
63 64
			return res.Emit(out)
		}
Jan Winkelmann's avatar
Jan Winkelmann committed
65

keks's avatar
keks committed
66 67 68 69
		fileOrder, _ := req.Options["file-order"].(bool)
		next, err := filestore.ListAll(fs, fileOrder)
		if err != nil {
			return err
70
		}
keks's avatar
keks committed
71 72 73

		out := listResToChan(req.Context, next)
		return res.Emit(out)
74
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
75
	PostRun: cmds.PostRunMap{
keks's avatar
keks committed
76 77 78 79 80 81
		cmds.CLI: func(res cmds.Response, re cmds.ResponseEmitter) error {
			var errors bool
			for {
				v, err := res.Next()
				if err != nil {
					if err == io.EOF {
Jan Winkelmann's avatar
Jan Winkelmann committed
82 83
						break
					}
keks's avatar
keks committed
84 85
					return err
				}
Jan Winkelmann's avatar
Jan Winkelmann committed
86

keks's avatar
keks committed
87 88
				r, ok := v.(*filestore.ListRes)
				if !ok {
keks's avatar
fixes  
keks committed
89
					return e.New(e.TypeErr(r, v))
90
				}
Jan Winkelmann's avatar
Jan Winkelmann committed
91

keks's avatar
keks committed
92 93 94 95 96
				if r.ErrorMsg != "" {
					errors = true
					fmt.Fprintf(os.Stderr, "%s\n", r.ErrorMsg)
				} else {
					fmt.Fprintf(os.Stdout, "%s\n", r.FormatLong())
Jan Winkelmann's avatar
Jan Winkelmann committed
97
				}
keks's avatar
keks committed
98 99 100 101 102
			}

			if errors {
				return fmt.Errorf("errors while displaying some entries")
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
103

keks's avatar
keks committed
104
			return nil
105
		},
106 107 108 109
	},
	Type: filestore.ListRes{},
}

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

			if r.Status == filestore.StatusOtherError {
				fmt.Fprintf(res.Stderr(), "%s\n", r.ErrorMsg)
177
			}
Jan Winkelmann's avatar
Jan Winkelmann committed
178
			fmt.Fprintf(res.Stdout(), "%s %s\n", r.Status.Format(), r.FormatLong())
179 180
			return nil, nil
		},
181 182 183 184
	},
	Type: filestore.ListRes{},
}

Jan Winkelmann's avatar
Jan Winkelmann committed
185 186
var dupsFileStore = &oldCmds.Command{
	Helptext: cmdkit.HelpText{
187
		Tagline: "List blocks that are both in the filestore and standard block storage.",
188
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
189 190
	Run: func(req oldCmds.Request, res oldCmds.Response) {
		_, fs, err := getFilestore(req.InvocContext())
191
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
192
			res.SetError(err, cmdkit.ErrNormal)
193 194 195 196
			return
		}
		ch, err := fs.FileManager().AllKeysChan(req.Context())
		if err != nil {
Jan Winkelmann's avatar
Jan Winkelmann committed
197
			res.SetError(err, cmdkit.ErrNormal)
198 199 200 201 202 203 204 205 206 207 208
			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 {
209 210 211 212
					select {
					case out <- &RefWrapper{Err: err.Error()}:
					case <-req.Context().Done():
					}
213 214 215
					return
				}
				if have {
216 217 218 219 220
					select {
					case out <- &RefWrapper{Ref: cid.String()}:
					case <-req.Context().Done():
						return
					}
221 222 223 224 225 226 227 228
				}
			}
		}()
	},
	Marshalers: refsMarshallerMap,
	Type:       RefWrapper{},
}

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

Jan Winkelmann's avatar
Jan Winkelmann committed
241
func listResToChan(ctx context.Context, next func() *filestore.ListRes) <-chan interface{} {
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
	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
}
259

260
func perKeyActionToChan(ctx context.Context, args []string, action func(cid.Cid) *filestore.ListRes) <-chan interface{} {
261 262 263 264 265 266
	out := make(chan interface{}, 128)
	go func() {
		defer close(out)
		for _, arg := range args {
			c, err := cid.Decode(arg)
			if err != nil {
267 268
				select {
				case out <- &filestore.ListRes{
269 270
					Status:   filestore.StatusOtherError,
					ErrorMsg: fmt.Sprintf("%s: %v", arg, err),
271 272
				}:
				case <-ctx.Done():
273
				}
274

275 276 277 278 279 280 281 282 283 284 285 286
				continue
			}
			r := action(c)
			select {
			case out <- r:
			case <-ctx.Done():
				return
			}
		}
	}()
	return out
}