Skip to content

Commit 247816a

Browse files
committed
feat(cmds): files: add new-root command to change the MFS root
1 parent 91c5265 commit 247816a

File tree

2 files changed

+118
-14
lines changed

2 files changed

+118
-14
lines changed

core/commands/files.go

Lines changed: 109 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ import (
1313
humanize "github.com/dustin/go-humanize"
1414
"github.com/ipfs/go-ipfs/core"
1515
"github.com/ipfs/go-ipfs/core/commands/cmdenv"
16+
"github.com/ipfs/go-ipfs/core/node"
17+
"github.com/ipfs/go-ipfs/repo/fsrepo"
1618

1719
bservice "github.com/ipfs/go-blockservice"
1820
cid "github.com/ipfs/go-cid"
1921
cidenc "github.com/ipfs/go-cidutil/cidenc"
22+
"github.com/ipfs/go-datastore"
2023
cmds "github.com/ipfs/go-ipfs-cmds"
2124
offline "github.com/ipfs/go-ipfs-exchange-offline"
2225
ipld "github.com/ipfs/go-ipld-format"
@@ -70,16 +73,17 @@ operations.
7073
cmds.BoolOption(filesFlushOptionName, "f", "Flush target and ancestors after write.").WithDefault(true),
7174
},
7275
Subcommands: map[string]*cmds.Command{
73-
"read": filesReadCmd,
74-
"write": filesWriteCmd,
75-
"mv": filesMvCmd,
76-
"cp": filesCpCmd,
77-
"ls": filesLsCmd,
78-
"mkdir": filesMkdirCmd,
79-
"stat": filesStatCmd,
80-
"rm": filesRmCmd,
81-
"flush": filesFlushCmd,
82-
"chcid": filesChcidCmd,
76+
"read": filesReadCmd,
77+
"write": filesWriteCmd,
78+
"mv": filesMvCmd,
79+
"cp": filesCpCmd,
80+
"ls": filesLsCmd,
81+
"mkdir": filesMkdirCmd,
82+
"stat": filesStatCmd,
83+
"rm": filesRmCmd,
84+
"flush": filesFlushCmd,
85+
"chcid": filesChcidCmd,
86+
"replace-root": filesReplaceRoot,
8387
},
8488
}
8589

@@ -1134,6 +1138,101 @@ Remove files or directories.
11341138
},
11351139
}
11361140

1141+
var filesReplaceRoot = &cmds.Command{
1142+
Helptext: cmds.HelpText{
1143+
// FIXME(BLOCKING): Somewhere around we should flag that this is an advanced command
1144+
// that you normally wouldn't need except in case of filesystem corruption. Where?
1145+
Tagline: "Replace the filesystem root.",
1146+
ShortDescription: `
1147+
Replace the filesystem root with another CID.
1148+
1149+
$ ipfs init
1150+
[...]
1151+
ipfs cat /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme # <- init dir
1152+
[...]
1153+
$ ipfs files ls /
1154+
[nothing; empty dir]
1155+
$ ipfs files stat / --hash
1156+
QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn
1157+
1158+
# FIXME(BLOCKING): Need the following to somehow "start" the root dir, otherwise
1159+
# the replace-root will fail to find the '/local/filesroot' entry in the repo
1160+
$ ipfs files cp /ipfs/QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc/readme /file
1161+
1162+
$ GOLOG_LOG_LEVEL="info" ipfs files replace-root QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc # init dir from before
1163+
[...] replaced MFS files root QmQPeNsJPyVWPFDVHb77w8G42Fvo15z4bG2X8D2GhfbSXc [...]
1164+
[here we have the CID of the old root to "undo" in case of error]
1165+
$ ipfs files ls /
1166+
[contents from init dir now set as the root of the filesystem]
1167+
about
1168+
contact
1169+
help
1170+
ping
1171+
quick-start
1172+
readme
1173+
security-notes
1174+
$ ipfs files replace-root QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn # empty dir from init
1175+
$ ipfs files ls /
1176+
[nothing; empty dir]
1177+
`,
1178+
},
1179+
Arguments: []cmds.Argument{
1180+
cmds.StringArg("new-root", true, false, "New root to use."),
1181+
},
1182+
// FIXME(BLOCKING): Can/should we do this with the repo running?
1183+
NoRemote: true,
1184+
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
1185+
if len(req.Arguments) < 1 {
1186+
fmt.Errorf("new root not provided")
1187+
}
1188+
newFilesRootCid, err := cid.Parse(req.Arguments[0])
1189+
if err != nil {
1190+
return fmt.Errorf("files root argument provided %s is not a valid CID: %w", req.Arguments[0], err)
1191+
1192+
}
1193+
// FIXME(BLOCKING): Check (a) that this CID exists *locally* and (b)
1194+
// that it's a dir.
1195+
1196+
cfgRoot, err := cmdenv.GetConfigRoot(env)
1197+
if err != nil {
1198+
return err
1199+
}
1200+
1201+
repo, err := fsrepo.Open(cfgRoot)
1202+
if err != nil {
1203+
return err
1204+
}
1205+
localDS := repo.Datastore()
1206+
defer repo.Close()
1207+
1208+
filesRootBytes, err := localDS.Get(req.Context, node.FilesRootDatastoreKey)
1209+
if err == datastore.ErrNotFound {
1210+
return fmt.Errorf("MFS files root %s not found in repo", node.FilesRootDatastoreKey)
1211+
} else if err != nil {
1212+
return fmt.Errorf("looking for MFS files root: %w", err)
1213+
}
1214+
filesRootCid, err := cid.Cast(filesRootBytes)
1215+
if err != nil {
1216+
return fmt.Errorf("casting MFS files root %s as CID: %w", filesRootBytes, err)
1217+
}
1218+
1219+
err = localDS.Put(req.Context, node.FilesRootDatastoreKey, newFilesRootCid.Bytes())
1220+
if err != nil {
1221+
return fmt.Errorf("storing new files root: %w", err)
1222+
}
1223+
// FIXME(BLOCKING): Do we need this if we're closing the repo at the end
1224+
// of the command? Likely not.
1225+
err = localDS.Sync(req.Context, node.FilesRootDatastoreKey)
1226+
if err != nil {
1227+
return fmt.Errorf("syncing new files root: %w", err)
1228+
}
1229+
1230+
log.Infof("replaced MFS files root %s with %s", filesRootCid, newFilesRootCid)
1231+
1232+
return nil
1233+
},
1234+
}
1235+
11371236
func getPrefixNew(req *cmds.Request) (cid.Builder, error) {
11381237
cidVer, cidVerSet := req.Options[filesCidVersionOptionName].(int)
11391238
hashFunStr, hashFunSet := req.Options[filesHashOptionName].(string)

core/node/core.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ import (
2929
"github.com/ipfs/go-ipfs/repo"
3030
)
3131

32+
var FilesRootDatastoreKey datastore.Key
33+
34+
func init() {
35+
FilesRootDatastoreKey = datastore.NewKey("/local/filesroot")
36+
}
37+
3238
// BlockService creates new blockservice which provides an interface to fetch content-addressable blocks
3339
func BlockService(lc fx.Lifecycle, bs blockstore.Blockstore, rem exchange.Interface) blockservice.BlockService {
3440
bsvc := blockservice.New(bs, rem)
@@ -110,7 +116,6 @@ func Dag(bs blockservice.BlockService) format.DAGService {
110116

111117
// Files loads persisted MFS root
112118
func Files(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo, dag format.DAGService) (*mfs.Root, error) {
113-
dsk := datastore.NewKey("/local/filesroot")
114119
pf := func(ctx context.Context, c cid.Cid) error {
115120
rootDS := repo.Datastore()
116121
if err := rootDS.Sync(ctx, blockstore.BlockPrefix); err != nil {
@@ -120,15 +125,15 @@ func Files(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo, dag format.
120125
return err
121126
}
122127

123-
if err := rootDS.Put(ctx, dsk, c.Bytes()); err != nil {
128+
if err := rootDS.Put(ctx, FilesRootDatastoreKey, c.Bytes()); err != nil {
124129
return err
125130
}
126-
return rootDS.Sync(ctx, dsk)
131+
return rootDS.Sync(ctx, FilesRootDatastoreKey)
127132
}
128133

129134
var nd *merkledag.ProtoNode
130135
ctx := helpers.LifecycleCtx(mctx, lc)
131-
val, err := repo.Datastore().Get(ctx, dsk)
136+
val, err := repo.Datastore().Get(ctx, FilesRootDatastoreKey)
132137

133138
switch {
134139
case err == datastore.ErrNotFound || val == nil:

0 commit comments

Comments
 (0)