@@ -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+
11371236func getPrefixNew (req * cmds.Request ) (cid.Builder , error ) {
11381237 cidVer , cidVerSet := req .Options [filesCidVersionOptionName ].(int )
11391238 hashFunStr , hashFunSet := req .Options [filesHashOptionName ].(string )
0 commit comments