@cryptotaxi247 / kubo / commits / d60cbc1c7

fix(mfs): respect Import config (#11273)

* test(deps): quick test of boxo with ipfs/boxo#1125 * test(mfs): verify CidBuilder preservation across mutations and restarts * docs(changelog): highlight MFS CidBuilder fix * fix(mfs): apply Import.CidVersion and HashFunction to MFS root The MFS root loaded at daemon startup never received a CidBuilder from config, so it stayed CIDv0/sha2-256 even with non-default Import settings. Pass the configured CidBuilder to mfs.NewRoot(). - add Import.UnixFSCidBuilder() helper for building cid.Prefix - pass WithCidBuilder to mfs.NewRoot in core/node/core.go - deduplicate getPrefixNew/getPrefix in files.go - strengthen regression test to check CID version and root dir * fix: restore explicit Flush, use upstream boxo - restore explicit Flush: false in addNode and addDir Mkdir calls that was dropped during the MkdirOpts refactor - use %q for hash function error message in getPrefix - switch from boxo fork replace to upstream boxo@98dabcc * fix(config): always build explicit CidBuilder from defaults UnixFSCidBuilder used to return nil when CidVersion and HashFunction matched compile-time defaults, relying on boxo's internal CIDv0/sha2-256 fallback. This will break when DefaultCidVersion changes to 1, because boxo will keep using CIDv0 regardless. - remove early-return short-circuit in UnixFSCidBuilder - add unit tests for explicit and default CidBuilder construction Refs: https://github.com/ipfs/kubo/issues/4143 * fix(files): reject chcid on MFS root path The MFS root CID format is now always set from Import.CidVersion and Import.HashFunction at startup, so chcid on "/" was silently overridden on every subsequent command or daemon restart. - chcid now requires a path argument and rejects "/" - help text and changelog explain how to change root CID format - sharness tests use Import config + daemon restarts instead of chcid / - added test for chcid on subdirectories with blake2b-256 * chore(deps): update boxo to latest main Picks up ipfs/boxo#1131: fix concurrent flush/close panic in MFS file descriptors (FUSE race condition). * fix(test): sharness daemon pairing and stale shard hash - add restart_daemon helper to avoid tripping t0015 meta-test that counts literal test_kill/test_launch pairs in each file - update cidv1 SHARD_HASH to match current boxo HAMT output --------- Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com>

Marcin Rataj committed Apr 7, 2026 at 18:50 UTC d60cbc1c74eb4694f5e56efea8e8f4ca5c5435b8
17 files changed +439 -236
AGENTS.md
+1
@@ -177,6 +177,7 @@ Run these steps in order before considering work complete:
177 - after editing CLI help text in `core/commands/`, verify width: `go test ./test/cli/... -run TestCommandDocsWidth`
178 - config options are documented in `docs/config.md`
179 - changelogs in `docs/changelogs/`: only edit the Table of Contents and the Highlights section; the Changelog and Contributors sections are auto-generated and must not be modified
180 +- avoid unnecessary line wrapping in `docs/changelogs/*`; let lines be long
181 - follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/)
182 - keep commit titles short and messages terse
183
config/import.go
+47
@@ -7,9 +7,12 @@ import (
7 "strings"
8
9 chunk "github.com/ipfs/boxo/chunker"
10 + merkledag "github.com/ipfs/boxo/ipld/merkledag"
11 "github.com/ipfs/boxo/ipld/unixfs/importer/helpers"
12 uio "github.com/ipfs/boxo/ipld/unixfs/io"
13 + "github.com/ipfs/boxo/mfs"
14 "github.com/ipfs/boxo/verifcid"
15 + cid "github.com/ipfs/go-cid"
16 mh "github.com/multiformats/go-multihash"
17 )
18
@@ -259,3 +262,47 @@ func (i *Import) UnixFSSplitterFunc() chunk.SplitterGen {
262 return s
263 }
264 }
265 +
266 +// MFSRootOptions returns all MFS root options derived from Import config.
267 +func (i *Import) MFSRootOptions() ([]mfs.Option, error) {
268 + cidBuilder, err := i.UnixFSCidBuilder()
269 + if err != nil {
270 + return nil, err
271 + }
272 + sizeEstimationMode := i.HAMTSizeEstimationMode()
273 + return []mfs.Option{
274 + mfs.WithCidBuilder(cidBuilder),
275 + mfs.WithChunker(i.UnixFSSplitterFunc()),
276 + mfs.WithMaxLinks(int(i.UnixFSDirectoryMaxLinks.WithDefault(DefaultUnixFSDirectoryMaxLinks))),
277 + mfs.WithMaxHAMTFanout(int(i.UnixFSHAMTDirectoryMaxFanout.WithDefault(DefaultUnixFSHAMTDirectoryMaxFanout))),
278 + mfs.WithHAMTShardingSize(int(i.UnixFSHAMTDirectorySizeThreshold.WithDefault(DefaultUnixFSHAMTDirectorySizeThreshold))),
279 + mfs.WithSizeEstimationMode(sizeEstimationMode),
280 + }, nil
281 +}
282 +
283 +// UnixFSCidBuilder returns a cid.Builder based on Import.CidVersion and
284 +// Import.HashFunction. Always builds an explicit prefix so that MFS
285 +// respects kubo defaults even when they differ from boxo's internal
286 +// CIDv0/sha2-256 default (see https://github.com/ipfs/kubo/issues/4143).
287 +func (i *Import) UnixFSCidBuilder() (cid.Builder, error) {
288 + cidVer := int(i.CidVersion.WithDefault(DefaultCidVersion))
289 + hashFunc := i.HashFunction.WithDefault(DefaultHashFunction)
290 +
291 + if hashFunc != DefaultHashFunction && cidVer == 0 {
292 + cidVer = 1
293 + }
294 +
295 + prefix, err := merkledag.PrefixForCidVersion(cidVer)
296 + if err != nil {
297 + return nil, err
298 + }
299 +
300 + hashCode, ok := mh.Names[strings.ToLower(hashFunc)]
301 + if !ok {
302 + return nil, fmt.Errorf("Import.HashFunction unrecognized: %q", hashFunc)
303 + }
304 + prefix.MhType = hashCode
305 + prefix.MhLength = -1
306 +
307 + return &prefix, nil
308 +}
config/import_test.go
+89
@@ -483,6 +483,95 @@ func TestValidateImportConfig_DAGLayout(t *testing.T) {
483 }
484 }
485
486 +func TestImport_UnixFSCidBuilder(t *testing.T) {
487 + defaultMhType := mh.Names[strings.ToLower(DefaultHashFunction)]
488 +
489 + tests := []struct {
490 + name string
491 + cfg Import
492 + wantCidVer uint64
493 + wantMhType uint64
494 + }{
495 + {
496 + name: "CIDv1 explicit",
497 + cfg: Import{CidVersion: *NewOptionalInteger(1)},
498 + wantCidVer: 1,
499 + wantMhType: defaultMhType,
500 + },
501 + {
502 + name: "CIDv0 explicit",
503 + cfg: Import{CidVersion: *NewOptionalInteger(0)},
504 + wantCidVer: 0,
505 + wantMhType: defaultMhType,
506 + },
507 + {
508 + name: "non-default hash upgrades CIDv0 to CIDv1",
509 + cfg: Import{HashFunction: *NewOptionalString("sha2-512")},
510 + wantCidVer: 1,
511 + wantMhType: mh.SHA2_512,
512 + },
513 + {
514 + name: "CIDv1 with sha2-512",
515 + cfg: Import{
516 + CidVersion: *NewOptionalInteger(1),
517 + HashFunction: *NewOptionalString("sha2-512"),
518 + },
519 + wantCidVer: 1,
520 + wantMhType: mh.SHA2_512,
521 + },
522 + }
523 +
524 + for _, tt := range tests {
525 + t.Run(tt.name, func(t *testing.T) {
526 + builder, err := tt.cfg.UnixFSCidBuilder()
527 + if err != nil {
528 + t.Fatalf("unexpected error: %v", err)
529 + }
530 + if builder == nil {
531 + t.Fatal("expected non-nil builder")
532 + }
533 + c, err := builder.Sum([]byte("test"))
534 + if err != nil {
535 + t.Fatalf("builder.Sum failed: %v", err)
536 + }
537 + pref := c.Prefix()
538 + if pref.Version != tt.wantCidVer {
539 + t.Errorf("CID version = %d, want %d", pref.Version, tt.wantCidVer)
540 + }
541 + if pref.MhType != tt.wantMhType {
542 + t.Errorf("multihash type = 0x%x, want 0x%x", pref.MhType, tt.wantMhType)
543 + }
544 + })
545 + }
546 +}
547 +
548 +// TestImport_UnixFSCidBuilderDefaults verifies that UnixFSCidBuilder always
549 +// returns an explicit builder even when no config is set, so that MFS
550 +// respects kubo's DefaultCidVersion rather than relying on boxo's internal
551 +// CIDv0 default (relevant for https://github.com/ipfs/kubo/issues/4143).
552 +func TestImport_UnixFSCidBuilderDefaults(t *testing.T) {
553 + cfg := &Import{}
554 + builder, err := cfg.UnixFSCidBuilder()
555 + if err != nil {
556 + t.Fatalf("unexpected error: %v", err)
557 + }
558 + if builder == nil {
559 + t.Fatal("expected non-nil builder at defaults")
560 + }
561 + c, err := builder.Sum([]byte("test"))
562 + if err != nil {
563 + t.Fatalf("builder.Sum failed: %v", err)
564 + }
565 + pref := c.Prefix()
566 + if pref.Version != uint64(DefaultCidVersion) {
567 + t.Errorf("CID version = %d, want DefaultCidVersion (%d)", pref.Version, DefaultCidVersion)
568 + }
569 + wantMhType := mh.Names[strings.ToLower(DefaultHashFunction)]
570 + if pref.MhType != wantMhType {
571 + t.Errorf("multihash type = 0x%x, want 0x%x (DefaultHashFunction=%s)", pref.MhType, wantMhType, DefaultHashFunction)
572 + }
573 +}
574 +
575 func TestImport_HAMTSizeEstimationMode(t *testing.T) {
576 tests := []struct {
577 cfg string
core/commands/files.go
+53 -91
@@ -28,7 +28,6 @@ import (
28 offline "github.com/ipfs/boxo/exchange/offline"
29 dag "github.com/ipfs/boxo/ipld/merkledag"
30 ft "github.com/ipfs/boxo/ipld/unixfs"
31 - uio "github.com/ipfs/boxo/ipld/unixfs/io"
31 mfs "github.com/ipfs/boxo/mfs"
32 "github.com/ipfs/boxo/path"
33 cid "github.com/ipfs/go-cid"
@@ -505,7 +504,7 @@ being GC'ed.
504 return err
505 }
506
508 - prefix, err := getPrefixNew(req, &cfg.Import)
507 + prefix, err := getPrefix(req, &cfg.Import)
508 if err != nil {
509 return err
510 }
@@ -558,7 +557,11 @@ being GC'ed.
557 if mkParents {
558 maxDirLinks := int(cfg.Import.UnixFSDirectoryMaxLinks.WithDefault(config.DefaultUnixFSDirectoryMaxLinks))
559 sizeEstimationMode := cfg.Import.HAMTSizeEstimationMode()
561 - err := ensureContainingDirectoryExists(nd.FilesRoot, dst, prefix, maxDirLinks, &sizeEstimationMode)
560 + err := ensureContainingDirectoryExists(nd.FilesRoot, dst,
561 + mfs.WithCidBuilder(prefix),
562 + mfs.WithMaxLinks(maxDirLinks),
563 + mfs.WithSizeEstimationMode(sizeEstimationMode),
564 + )
565 if err != nil {
566 return err
567 }
@@ -1060,7 +1063,7 @@ See '--to-files' in 'ipfs add --help' for more information.
1063 rawLeaves = cfg.Import.UnixFSRawLeaves.WithDefault(config.DefaultUnixFSRawLeaves)
1064 }
1065
1063 - prefix, err := getPrefixNew(req, &cfg.Import)
1066 + prefix, err := getPrefix(req, &cfg.Import)
1067 if err != nil {
1068 return err
1069 }
@@ -1073,7 +1076,11 @@ See '--to-files' in 'ipfs add --help' for more information.
1076 if mkParents {
1077 maxDirLinks := int(cfg.Import.UnixFSDirectoryMaxLinks.WithDefault(config.DefaultUnixFSDirectoryMaxLinks))
1078 sizeEstimationMode := cfg.Import.HAMTSizeEstimationMode()
1076 - err := ensureContainingDirectoryExists(nd.FilesRoot, path, prefix, maxDirLinks, &sizeEstimationMode)
1079 + err := ensureContainingDirectoryExists(nd.FilesRoot, path,
1080 + mfs.WithCidBuilder(prefix),
1081 + mfs.WithMaxLinks(maxDirLinks),
1082 + mfs.WithSizeEstimationMode(sizeEstimationMode),
1083 + )
1084 if err != nil {
1085 return err
1086 }
@@ -1203,13 +1210,11 @@ Examples:
1210 maxDirLinks := int(cfg.Import.UnixFSDirectoryMaxLinks.WithDefault(config.DefaultUnixFSDirectoryMaxLinks))
1211 sizeEstimationMode := cfg.Import.HAMTSizeEstimationMode()
1212
1206 - err = mfs.Mkdir(root, dirtomake, mfs.MkdirOpts{
1207 - Mkparents: dashp,
1208 - Flush: flush,
1209 - CidBuilder: prefix,
1210 - MaxLinks: maxDirLinks,
1211 - SizeEstimationMode: &sizeEstimationMode,
1212 - })
1213 + err = mfs.Mkdir(root, dirtomake, mfs.MkdirOpts{Mkparents: dashp, Flush: flush},
1214 + mfs.WithCidBuilder(prefix),
1215 + mfs.WithMaxLinks(maxDirLinks),
1216 + mfs.WithSizeEstimationMode(sizeEstimationMode),
1217 + )
1218
1219 return err
1220 },
@@ -1264,10 +1269,15 @@ var filesChcidCmd = &cmds.Command{
1269 Tagline: "Change the CID version or hash function of the root node of a given path.",
1270 ShortDescription: `
1271 Change the CID version or hash function of the root node of a given path.
1272 +
1273 +Note: the MFS root ('/') CID format is controlled by Import.CidVersion and
1274 +Import.HashFunction in the config and cannot be changed with this command.
1275 +Use 'ipfs config' to modify these values instead. This command only works
1276 +on subdirectories of the MFS root.
1277 `,
1278 },
1279 Arguments: []cmds.Argument{
1270 - cmds.StringArg("path", false, false, "Path to change. Default: '/'."),
1280 + cmds.StringArg("path", true, false, "Path to change (must not be '/')."),
1281 },
1282 Options: []cmds.Option{
1283 cidVersionOption,
@@ -1279,9 +1289,10 @@ Change the CID version or hash function of the root node of a given path.
1289 return err
1290 }
1291
1282 - path := "/"
1283 - if len(req.Arguments) > 0 {
1284 - path = req.Arguments[0]
1292 + path := req.Arguments[0]
1293 + if path == "/" {
1294 + return fmt.Errorf("cannot change CID format of the MFS root; " +
1295 + "use 'ipfs config Import.CidVersion' and 'ipfs config Import.HashFunction' instead")
1296 }
1297
1298 flush, _ := req.Options[filesFlushOptionName].(bool)
@@ -1446,97 +1457,48 @@ func removePath(filesRoot *mfs.Root, path string, force bool, dashr bool) error
1457 return pdir.Flush()
1458 }
1459
1449 -func getPrefixNew(req *cmds.Request, importCfg *config.Import) (cid.Builder, error) {
1450 - cidVer, cidVerSet := req.Options[filesCidVersionOptionName].(int)
1451 - hashFunStr, hashFunSet := req.Options[filesHashOptionName].(string)
1452 -
1453 - // Fall back to Import config if CLI options not set
1454 - if !cidVerSet && importCfg != nil && !importCfg.CidVersion.IsDefault() {
1455 - cidVer = int(importCfg.CidVersion.WithDefault(config.DefaultCidVersion))
1456 - cidVerSet = true
1457 - }
1458 - if !hashFunSet && importCfg != nil && !importCfg.HashFunction.IsDefault() {
1459 - hashFunStr = importCfg.HashFunction.WithDefault(config.DefaultHashFunction)
1460 - hashFunSet = true
1461 - }
1462 -
1463 - if !cidVerSet && !hashFunSet {
1464 - return nil, nil
1465 - }
1466 -
1467 - if hashFunSet && cidVer == 0 {
1468 - cidVer = 1
1469 - }
1470 -
1471 - prefix, err := dag.PrefixForCidVersion(cidVer)
1472 - if err != nil {
1473 - return nil, err
1474 - }
1475 -
1476 - if hashFunSet {
1477 - hashFunCode, ok := mh.Names[strings.ToLower(hashFunStr)]
1478 - if !ok {
1479 - return nil, fmt.Errorf("unrecognized hash function: %s", strings.ToLower(hashFunStr))
1480 - }
1481 - prefix.MhType = hashFunCode
1482 - prefix.MhLength = -1
1483 - }
1484 -
1485 - return &prefix, nil
1486 -}
1487 -
1460 +// getPrefix builds a cid.Builder from CLI flags, falling back to importCfg
1461 +// when provided. Returns (nil, nil) when neither CLI nor config set a value.
1462 func getPrefix(req *cmds.Request, importCfg *config.Import) (cid.Builder, error) {
1463 cidVer, cidVerSet := req.Options[filesCidVersionOptionName].(int)
1464 hashFunStr, hashFunSet := req.Options[filesHashOptionName].(string)
1465
1492 - // Fall back to Import config if CLI options not set
1493 - if !cidVerSet && importCfg != nil && !importCfg.CidVersion.IsDefault() {
1494 - cidVer = int(importCfg.CidVersion.WithDefault(config.DefaultCidVersion))
1495 - cidVerSet = true
1496 - }
1497 - if !hashFunSet && importCfg != nil && !importCfg.HashFunction.IsDefault() {
1498 - hashFunStr = importCfg.HashFunction.WithDefault(config.DefaultHashFunction)
1499 - hashFunSet = true
1500 - }
1501 -
1502 - if !cidVerSet && !hashFunSet {
1503 - return nil, nil
1504 - }
1505 -
1506 - if hashFunSet && cidVer == 0 {
1507 - cidVer = 1
1508 - }
1509 -
1510 - prefix, err := dag.PrefixForCidVersion(cidVer)
1511 - if err != nil {
1512 - return nil, err
1466 + if cidVerSet || hashFunSet {
1467 + // CLI flags take precedence: build prefix from them directly.
1468 + if hashFunSet && cidVer == 0 {
1469 + cidVer = 1
1470 + }
1471 + prefix, err := dag.PrefixForCidVersion(cidVer)
1472 + if err != nil {
1473 + return nil, err
1474 + }
1475 + if hashFunSet {
1476 + hashFunCode, ok := mh.Names[strings.ToLower(hashFunStr)]
1477 + if !ok {
1478 + return nil, fmt.Errorf("unrecognized hash function: %q", hashFunStr)
1479 + }
1480 + prefix.MhType = hashFunCode
1481 + prefix.MhLength = -1
1482 + }
1483 + return &prefix, nil
1484 }
1485
1515 - if hashFunSet {
1516 - hashFunCode, ok := mh.Names[strings.ToLower(hashFunStr)]
1517 - if !ok {
1518 - return nil, fmt.Errorf("unrecognized hash function: %s", strings.ToLower(hashFunStr))
1519 - }
1520 - prefix.MhType = hashFunCode
1521 - prefix.MhLength = -1
1486 + // No CLI flags: fall back to Import config.
1487 + if importCfg != nil {
1488 + return importCfg.UnixFSCidBuilder()
1489 }
1490
1524 - return &prefix, nil
1491 + return nil, nil
1492 }
1493
1527 -func ensureContainingDirectoryExists(r *mfs.Root, path string, builder cid.Builder, maxLinks int, sizeEstimationMode *uio.SizeEstimationMode) error {
1494 +func ensureContainingDirectoryExists(r *mfs.Root, path string, opts ...mfs.Option) error {
1495 dirtomake := gopath.Dir(path)
1496
1497 if dirtomake == "/" {
1498 return nil
1499 }
1500
1534 - return mfs.Mkdir(r, dirtomake, mfs.MkdirOpts{
1535 - Mkparents: true,
1536 - CidBuilder: builder,
1537 - MaxLinks: maxLinks,
1538 - SizeEstimationMode: sizeEstimationMode,
1539 - })
1501 + return mfs.Mkdir(r, dirtomake, mfs.MkdirOpts{Mkparents: true}, opts...)
1502 }
1503
1504 func getFileHandle(r *mfs.Root, path string, create bool, builder cid.Builder) (*mfs.File, error) {
core/coreunix/add.go
+21 -34
@@ -107,12 +107,7 @@ func (adder *Adder) mfsRoot() (*mfs.Root, error) {
107 }
108
109 // Note, this adds it to DAGService already.
110 - mr, err := mfs.NewEmptyRoot(adder.ctx, adder.dagService, nil, nil, mfs.MkdirOpts{
111 - CidBuilder: adder.CidBuilder,
112 - MaxLinks: adder.MaxDirectoryLinks,
113 - MaxHAMTFanout: adder.MaxHAMTFanout,
114 - SizeEstimationMode: adder.SizeEstimationMode,
115 - })
110 + mr, err := mfs.NewEmptyRoot(adder.ctx, adder.dagService, nil, nil, adder.mkdirOpts()...)
111 if err != nil {
112 return nil, err
113 }
@@ -125,6 +120,20 @@ func (adder *Adder) SetMfsRoot(r *mfs.Root) {
120 adder.mroot = r
121 }
122
123 +// mkdirOpts returns MFS options derived from the adder's config,
124 +// with any additional options appended.
125 +func (adder *Adder) mkdirOpts(extra ...mfs.Option) []mfs.Option {
126 + opts := []mfs.Option{
127 + mfs.WithCidBuilder(adder.CidBuilder),
128 + mfs.WithMaxLinks(adder.MaxDirectoryLinks),
129 + mfs.WithMaxHAMTFanout(adder.MaxHAMTFanout),
130 + }
131 + if adder.SizeEstimationMode != nil {
132 + opts = append(opts, mfs.WithSizeEstimationMode(*adder.SizeEstimationMode))
133 + }
134 + return append(opts, extra...)
135 +}
136 +
137 // Constructs a node from reader's data, and adds it. Doesn't pin.
138 func (adder *Adder) add(reader io.Reader) (ipld.Node, error) {
139 chnk, err := chunker.FromString(reader, adder.Chunker)
@@ -274,15 +283,8 @@ func (adder *Adder) addNode(node ipld.Node, path string) error {
283
284 dir := gopath.Dir(path)
285 if dir != "." {
277 - opts := mfs.MkdirOpts{
278 - Mkparents: true,
279 - Flush: false,
280 - CidBuilder: adder.CidBuilder,
281 - MaxLinks: adder.MaxDirectoryLinks,
282 - MaxHAMTFanout: adder.MaxHAMTFanout,
283 - SizeEstimationMode: adder.SizeEstimationMode,
284 - }
285 - if err := mfs.Mkdir(mr, dir, opts); err != nil {
286 + mkdirOpts := adder.mkdirOpts()
287 + if err := mfs.Mkdir(mr, dir, mfs.MkdirOpts{Mkparents: true, Flush: false}, mkdirOpts...); err != nil {
288 return err
289 }
290 }
@@ -506,15 +508,8 @@ func (adder *Adder) addDir(ctx context.Context, path string, dir files.Directory
508
509 // if we need to store mode or modification time then create a new root which includes that data
510 if toplevel && (adder.FileMode != 0 || !adder.FileMtime.IsZero()) {
509 - mr, err := mfs.NewEmptyRoot(ctx, adder.dagService, nil, nil,
510 - mfs.MkdirOpts{
511 - CidBuilder: adder.CidBuilder,
512 - MaxLinks: adder.MaxDirectoryLinks,
513 - MaxHAMTFanout: adder.MaxHAMTFanout,
514 - ModTime: adder.FileMtime,
515 - Mode: adder.FileMode,
516 - SizeEstimationMode: adder.SizeEstimationMode,
517 - })
511 + opts := adder.mkdirOpts(mfs.WithMode(adder.FileMode), mfs.WithModTime(adder.FileMtime))
512 + mr, err := mfs.NewEmptyRoot(ctx, adder.dagService, nil, nil, opts...)
513 if err != nil {
514 return err
515 }
@@ -526,16 +521,8 @@ func (adder *Adder) addDir(ctx context.Context, path string, dir files.Directory
521 if err != nil {
522 return err
523 }
529 - err = mfs.Mkdir(mr, path, mfs.MkdirOpts{
530 - Mkparents: true,
531 - Flush: false,
532 - CidBuilder: adder.CidBuilder,
533 - Mode: adder.FileMode,
534 - ModTime: adder.FileMtime,
535 - MaxLinks: adder.MaxDirectoryLinks,
536 - MaxHAMTFanout: adder.MaxHAMTFanout,
537 - SizeEstimationMode: adder.SizeEstimationMode,
538 - })
524 + mkdirOpts := adder.mkdirOpts(mfs.WithMode(adder.FileMode), mfs.WithModTime(adder.FileMtime))
525 + err = mfs.Mkdir(mr, path, mfs.MkdirOpts{Mkparents: true, Flush: false}, mkdirOpts...)
526 if err != nil {
527 return err
528 }
core/node/core.go
+6 -13
@@ -248,19 +248,12 @@ func Files(strategy string) func(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo
248 if err != nil {
249 return nil, fmt.Errorf("failed to get config: %w", err)
250 }
251 - chunkerGen := cfg.Import.UnixFSSplitterFunc()
252 - maxDirLinks := int(cfg.Import.UnixFSDirectoryMaxLinks.WithDefault(config.DefaultUnixFSDirectoryMaxLinks))
253 - maxHAMTFanout := int(cfg.Import.UnixFSHAMTDirectoryMaxFanout.WithDefault(config.DefaultUnixFSHAMTDirectoryMaxFanout))
254 - hamtShardingSize := int(cfg.Import.UnixFSHAMTDirectorySizeThreshold.WithDefault(config.DefaultUnixFSHAMTDirectorySizeThreshold))
255 - sizeEstimationMode := cfg.Import.HAMTSizeEstimationMode()
256 -
257 - root, err := mfs.NewRoot(ctx, dag, nd, pf, prov,
258 - mfs.WithChunker(chunkerGen),
259 - mfs.WithMaxLinks(maxDirLinks),
260 - mfs.WithMaxHAMTFanout(maxHAMTFanout),
261 - mfs.WithHAMTShardingSize(hamtShardingSize),
262 - mfs.WithSizeEstimationMode(sizeEstimationMode),
263 - )
251 + mfsOpts, err := cfg.Import.MFSRootOptions()
252 + if err != nil {
253 + return nil, fmt.Errorf("failed to build MFS options from Import config: %w", err)
254 + }
255 +
256 + root, err := mfs.NewRoot(ctx, dag, nd, pf, prov, mfsOpts...)
257 if err != nil {
258 return nil, fmt.Errorf("failed to initialize MFS root from %s stored at %s: %w. "+
259 "If corrupted, use 'ipfs files chroot' to reset (see --help)", nd.Cid(), FilesRootDatastoreKey, err)
docs/changelogs/v0.41.md
+11 -2
@@ -14,7 +14,8 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team.
14 - [✨ New `ipfs cid inspect` command](#-new-ipfs-cid-inspect-command)
15 - [🖥️ WebUI Improvements](#-webui-improvements)
16 - [🔧 Correct provider addresses for custom HTTP routing](#-correct-provider-addresses-for-custom-http-routing)
17 - - [`ipfs object patch` validates UnixFS node types](#ipfs-object-patch-validates-unixfs-node-types)
17 + - [🛡️ `ipfs object patch` validates UnixFS node types](#-ipfs-object-patch-validates-unixfs-node-types)
18 + - [🔗 MFS: fixed CidBuilder preservation](#-mfs-fixed-cidbuilder-preservation)
19 - [📂 FUSE Mount Fixes](#-fuse-mount-fixes)
20 - [📦️ Dependency updates](#-dependency-updates)
21 - [📝 Changelog](#-changelog)
@@ -78,7 +79,7 @@ Peer locations load faster thanks to UX optimizations in the underlying ipfs-geo
79
80 Nodes using custom routing (`Routing.Type=custom`) with [IPIP-526](https://github.com/ipfs/specs/pull/526) could end up publishing unresolved `0.0.0.0` addresses in provider records. Addresses are now resolved at provide-time, and when AutoNAT V2 has confirmed publicly reachable addresses, those are preferred automatically. See [#11213](https://github.com/ipfs/kubo/issues/11213).
81
81 -#### `ipfs object patch` validates UnixFS node types
82 +#### 🛡️ `ipfs object patch` validates UnixFS node types
83
84 As part of the ongoing deprecation of the legacy `ipfs object` API (which
85 predates HAMTShard directories and CIDv1), the `add-link` and `rm-link`
@@ -99,6 +100,14 @@ directory types correctly, including large sharded directories.
100
101 A `--allow-non-unixfs` flag is available on both `ipfs object patch` commands to bypass validation.
102
103 +#### 🔗 MFS: fixed CidBuilder preservation
104 +
105 +`ipfs files` commands now correctly preserve the configured CID version and hash function (`Import.CidVersion`, `Import.HashFunction`) in all MFS operations. Previously, the `CidBuilder` could be silently lost when modifying file contents, creating nested directories with `mkdir -p`, or restarting the daemon, causing some entries to fall back to CIDv0/sha2-256.
106 +
107 +Additionally, the MFS root directory itself now respects [`Import.CidVersion`](https://github.com/ipfs/kubo/blob/master/docs/config.md#importcidversion) and [`Import.HashFunction`](https://github.com/ipfs/kubo/blob/master/docs/config.md#importhashfunction) at daemon startup. Before this fix, the root always used CIDv0/sha2-256 regardless of config. Because the MFS root CID format is now managed by these config options, `ipfs files chcid` no longer accepts the root path `/`. It continues to work on subdirectories.
108 +
109 +See [boxo#1125](https://github.com/ipfs/boxo/pull/1125) and [kubo#11273](https://github.com/ipfs/kubo/pull/11273).
110 +
111 #### 📂 FUSE Mount Fixes
112
113 FUSE mounts (`/ipfs`, `/ipns`, `/mfs`) now work with editors like VIM that rely on `fsync` and expect standard file ownership. FUSE support is still experimental. If you run into problems, please report them at [kubo/issues](https://github.com/ipfs/kubo/issues).
docs/examples/kubo-as-a-library/go.mod
+11 -11
@@ -7,7 +7,7 @@ go 1.25.7
7 replace github.com/ipfs/kubo => ./../../..
8
9 require (
10 - github.com/ipfs/boxo v0.37.1-0.20260317235537-851246983422
10 + github.com/ipfs/boxo v0.37.1-0.20260407154542-a9db6465a5ae
11 github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
12 github.com/libp2p/go-libp2p v0.48.0
13 github.com/multiformats/go-multiaddr v0.16.1
@@ -67,7 +67,7 @@ require (
67 github.com/google/gopacket v1.1.19 // indirect
68 github.com/google/uuid v1.6.0 // indirect
69 github.com/gorilla/websocket v1.5.3 // indirect
70 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
70 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
71 github.com/guillaumemichel/reservedpool v0.3.0 // indirect
72 github.com/hashicorp/golang-lru v1.0.2 // indirect
73 github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
@@ -192,14 +192,14 @@ require (
192 github.com/zeebo/blake3 v0.2.4 // indirect
193 go.opencensus.io v0.24.0 // indirect
194 go.opentelemetry.io/auto/sdk v1.2.1 // indirect
195 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect
195 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
196 go.opentelemetry.io/otel v1.42.0 // indirect
197 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
198 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect
199 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect
200 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 // indirect
197 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect
198 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 // indirect
199 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 // indirect
200 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 // indirect
201 go.opentelemetry.io/otel/metric v1.42.0 // indirect
202 - go.opentelemetry.io/otel/sdk v1.40.0 // indirect
202 + go.opentelemetry.io/otel/sdk v1.42.0 // indirect
203 go.opentelemetry.io/otel/trace v1.42.0 // indirect
204 go.opentelemetry.io/proto/otlp v1.9.0 // indirect
205 go.uber.org/dig v1.19.0 // indirect
@@ -222,9 +222,9 @@ require (
222 golang.org/x/tools v0.43.0 // indirect
223 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
224 gonum.org/v1/gonum v0.17.0 // indirect
225 - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
226 - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
227 - google.golang.org/grpc v1.78.0 // indirect
225 + google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
226 + google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
227 + google.golang.org/grpc v1.79.2 // indirect
228 google.golang.org/protobuf v1.36.11 // indirect
229 gopkg.in/yaml.v3 v3.0.1 // indirect
230 lukechampine.com/blake3 v1.4.1 // indirect
docs/examples/kubo-as-a-library/go.sum
+24 -24
@@ -310,8 +310,8 @@ github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWS
310 github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
311 github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
312 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
313 -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
314 -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
313 +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
314 +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
315 github.com/guillaumemichel/reservedpool v0.3.0 h1:eqqO/QvTllLBrit7LVtVJBqw4cD0WdV9ajUe7WNTajw=
316 github.com/guillaumemichel/reservedpool v0.3.0/go.mod h1:sXSDIaef81TFdAJglsCFCMfgF5E5Z5xK1tFhjDhvbUc=
317 github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
@@ -352,8 +352,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 h1:OqNqsGZPX8zh3eFMO8Lf8EHRRnSGBMqcd
352 github.com/ipfs-shipyard/nopfs/ipfs v0.25.0/go.mod h1:BxhUdtBgOXg1B+gAPEplkg/GpyTZY+kCMSfsJvvydqU=
353 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
354 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
355 -github.com/ipfs/boxo v0.37.1-0.20260317235537-851246983422 h1:yY3ot/DU1bqTzHDBARACM76Tbx9s4xzcRbzifG1e/es=
356 -github.com/ipfs/boxo v0.37.1-0.20260317235537-851246983422/go.mod h1:8yyiRn54F2CsW13n0zwXEPrVsZix/gFj9SYIRYMZ6KE=
355 +github.com/ipfs/boxo v0.37.1-0.20260407154542-a9db6465a5ae h1:0MsWL16G8bSkke0364AJRYmSLc26JfVfWw01cVro+7Q=
356 +github.com/ipfs/boxo v0.37.1-0.20260407154542-a9db6465a5ae/go.mod h1:9fqW+YoaAEnhdZgXQo1PmzNWNX5evTu8fmVEQV51ksE=
357 github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
358 github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
359 github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk=
@@ -829,24 +829,24 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
829 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
830 go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
831 go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
832 -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
833 -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
832 +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
833 +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
834 go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
835 go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
836 -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
837 -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
838 -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
839 -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
840 -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc=
841 -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
842 -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
843 -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
836 +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
837 +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
838 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto=
839 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU=
840 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 h1:uLXP+3mghfMf7XmV4PkGfFhFKuNWoCvvx5wP/wOXo0o=
841 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0/go.mod h1:v0Tj04armyT59mnURNUJf7RCKcKzq+lgJs6QSjHjaTc=
842 +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw=
843 +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs=
844 go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
845 go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
846 -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
847 -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
848 -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
849 -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
846 +go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
847 +go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
848 +go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
849 +go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
850 go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
851 go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
852 go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
@@ -1242,10 +1242,10 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
1242 google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
1243 google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
1244 google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
1245 -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
1246 -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
1247 -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
1248 -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
1245 +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
1246 +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
1247 +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
1248 +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
1249 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
1250 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
1251 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1266,8 +1266,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
1266 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
1267 google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
1268 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
1269 -google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
1270 -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
1269 +google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
1270 +google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
1271 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
1272 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
1273 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
fuse/ipns/ipns_unix.go
+6 -6
@@ -45,12 +45,12 @@ type FileSystem struct {
45 }
46
47 // NewFileSystem constructs new fs using given core.IpfsNode instance.
48 -func NewFileSystem(ctx context.Context, ipfs iface.CoreAPI, ipfspath, ipnspath string) (*FileSystem, error) {
48 +func NewFileSystem(ctx context.Context, ipfs iface.CoreAPI, ipfspath, ipnspath string, mfsOpts ...mfs.Option) (*FileSystem, error) {
49 key, err := ipfs.Key().Self(ctx)
50 if err != nil {
51 return nil, err
52 }
53 - root, err := CreateRoot(ctx, ipfs, map[string]iface.Key{"local": key}, ipfspath, ipnspath)
53 + root, err := CreateRoot(ctx, ipfs, map[string]iface.Key{"local": key}, ipfspath, ipnspath, mfsOpts...)
54 if err != nil {
55 return nil, err
56 }
@@ -96,7 +96,7 @@ func ipnsPubFunc(ipfs iface.CoreAPI, key iface.Key) mfs.PubFunc {
96 }
97 }
98
99 -func loadRoot(ctx context.Context, ipfs iface.CoreAPI, key iface.Key) (*mfs.Root, fs.Node, error) {
99 +func loadRoot(ctx context.Context, ipfs iface.CoreAPI, key iface.Key, mfsOpts ...mfs.Option) (*mfs.Root, fs.Node, error) {
100 node, err := ipfs.ResolveNode(ctx, key.Path())
101 switch err {
102 case nil:
@@ -115,7 +115,7 @@ func loadRoot(ctx context.Context, ipfs iface.CoreAPI, key iface.Key) (*mfs.Root
115 // We have no access to provider.System from the CoreAPI. The Routing
116 // part offers Provide through the router so it may be slow/risky
117 // to give that here to MFS. Therefore we leave as nil.
118 - root, err := mfs.NewRoot(ctx, ipfs.Dag(), pbnode, ipnsPubFunc(ipfs, key), nil)
118 + root, err := mfs.NewRoot(ctx, ipfs.Dag(), pbnode, ipnsPubFunc(ipfs, key), nil, mfsOpts...)
119 if err != nil {
120 return nil, nil, err
121 }
@@ -123,12 +123,12 @@ func loadRoot(ctx context.Context, ipfs iface.CoreAPI, key iface.Key) (*mfs.Root
123 return root, &Directory{dir: root.GetDirectory()}, nil
124 }
125
126 -func CreateRoot(ctx context.Context, ipfs iface.CoreAPI, keys map[string]iface.Key, ipfspath, ipnspath string) (*Root, error) {
126 +func CreateRoot(ctx context.Context, ipfs iface.CoreAPI, keys map[string]iface.Key, ipfspath, ipnspath string, mfsOpts ...mfs.Option) (*Root, error) {
127 ldirs := make(map[string]fs.Node)
128 roots := make(map[string]*mfs.Root)
129 links := make(map[string]*Link)
130 for alias, k := range keys {
131 - root, fsn, err := loadRoot(ctx, ipfs, k)
131 + root, fsn, err := loadRoot(ctx, ipfs, k, mfsOpts...)
132 if err != nil {
133 return nil, err
134 }
fuse/ipns/mount_unix.go
+6 -1
@@ -22,7 +22,12 @@ func Mount(ipfs *core.IpfsNode, ipnsmp, ipfsmp string) (mount.Mount, error) {
22
23 allowOther := cfg.Mounts.FuseAllowOther
24
25 - fsys, err := NewFileSystem(ipfs.Context(), coreAPI, ipfsmp, ipnsmp)
25 + mfsOpts, err := cfg.Import.MFSRootOptions()
26 + if err != nil {
27 + return nil, err
28 + }
29 +
30 + fsys, err := NewFileSystem(ipfs.Context(), coreAPI, ipfsmp, ipnsmp, mfsOpts...)
31 if err != nil {
32 return nil, err
33 }
go.mod
+12 -12
@@ -21,7 +21,7 @@ require (
21 github.com/hashicorp/go-version v1.9.0
22 github.com/ipfs-shipyard/nopfs v0.0.14
23 github.com/ipfs-shipyard/nopfs/ipfs v0.25.0
24 - github.com/ipfs/boxo v0.37.1-0.20260317235537-851246983422
24 + github.com/ipfs/boxo v0.37.1-0.20260407154542-a9db6465a5ae
25 github.com/ipfs/go-block-format v0.2.3
26 github.com/ipfs/go-cid v0.6.0
27 github.com/ipfs/go-cidutil v0.1.1
@@ -77,12 +77,12 @@ require (
77 github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1
78 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
79 go.opencensus.io v0.24.0
80 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0
80 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0
81 go.opentelemetry.io/contrib/propagators/autoprop v0.46.1
82 go.opentelemetry.io/otel v1.42.0
83 go.opentelemetry.io/otel/exporters/prometheus v0.56.0
84 - go.opentelemetry.io/otel/sdk v1.40.0
85 - go.opentelemetry.io/otel/sdk/metric v1.40.0
84 + go.opentelemetry.io/otel/sdk v1.42.0
85 + go.opentelemetry.io/otel/sdk/metric v1.42.0
86 go.opentelemetry.io/otel/trace v1.42.0
87 go.uber.org/dig v1.19.0
88 go.uber.org/fx v1.24.0
@@ -146,7 +146,7 @@ require (
146 github.com/google/gopacket v1.1.19 // indirect
147 github.com/gorilla/mux v1.8.1 // indirect
148 github.com/gorilla/websocket v1.5.3 // indirect
149 - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
149 + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
150 github.com/guillaumemichel/reservedpool v0.3.0 // indirect
151 github.com/hashicorp/golang-lru v1.0.2 // indirect
152 github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
@@ -244,10 +244,10 @@ require (
244 go.opentelemetry.io/contrib/propagators/b3 v1.21.1 // indirect
245 go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 // indirect
246 go.opentelemetry.io/contrib/propagators/ot v1.21.1 // indirect
247 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect
248 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect
249 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect
250 - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 // indirect
247 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect
248 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 // indirect
249 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 // indirect
250 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 // indirect
251 go.opentelemetry.io/otel/metric v1.42.0 // indirect
252 go.opentelemetry.io/proto/otlp v1.9.0 // indirect
253 go.uber.org/mock v0.5.2 // indirect
@@ -264,9 +264,9 @@ require (
264 golang.org/x/tools v0.43.0 // indirect
265 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
266 gonum.org/v1/gonum v0.17.0 // indirect
267 - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
268 - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
269 - google.golang.org/grpc v1.78.0 // indirect
267 + google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
268 + google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
269 + google.golang.org/grpc v1.79.2 // indirect
270 gopkg.in/yaml.v2 v2.4.0 // indirect
271 gopkg.in/yaml.v3 v3.0.1 // indirect
272 lukechampine.com/blake3 v1.4.1 // indirect
go.sum
+24 -24
@@ -348,8 +348,8 @@ github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWS
348 github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
349 github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
350 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
351 -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
352 -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
351 +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
352 +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
353 github.com/guillaumemichel/reservedpool v0.3.0 h1:eqqO/QvTllLBrit7LVtVJBqw4cD0WdV9ajUe7WNTajw=
354 github.com/guillaumemichel/reservedpool v0.3.0/go.mod h1:sXSDIaef81TFdAJglsCFCMfgF5E5Z5xK1tFhjDhvbUc=
355 github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU=
@@ -392,8 +392,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 h1:OqNqsGZPX8zh3eFMO8Lf8EHRRnSGBMqcd
392 github.com/ipfs-shipyard/nopfs/ipfs v0.25.0/go.mod h1:BxhUdtBgOXg1B+gAPEplkg/GpyTZY+kCMSfsJvvydqU=
393 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
394 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
395 -github.com/ipfs/boxo v0.37.1-0.20260317235537-851246983422 h1:yY3ot/DU1bqTzHDBARACM76Tbx9s4xzcRbzifG1e/es=
396 -github.com/ipfs/boxo v0.37.1-0.20260317235537-851246983422/go.mod h1:8yyiRn54F2CsW13n0zwXEPrVsZix/gFj9SYIRYMZ6KE=
395 +github.com/ipfs/boxo v0.37.1-0.20260407154542-a9db6465a5ae h1:0MsWL16G8bSkke0364AJRYmSLc26JfVfWw01cVro+7Q=
396 +github.com/ipfs/boxo v0.37.1-0.20260407154542-a9db6465a5ae/go.mod h1:9fqW+YoaAEnhdZgXQo1PmzNWNX5evTu8fmVEQV51ksE=
397 github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
398 github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
399 github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk=
@@ -953,8 +953,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
953 go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
954 go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
955 go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
956 -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
957 -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
956 +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
957 +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
958 go.opentelemetry.io/contrib/propagators/autoprop v0.46.1 h1:cXTYcMjY0dsYokAuo8LbNBQxpF8VgTHdiHJJ1zlIXl4=
959 go.opentelemetry.io/contrib/propagators/autoprop v0.46.1/go.mod h1:WZxgny1/6+j67B1s72PLJ4bGjidoWFzSmLNfJKVt2bo=
960 go.opentelemetry.io/contrib/propagators/aws v1.21.1 h1:uQIQIDWb0gzyvon2ICnghpLAf9w7ADOCUiIiwCQgR2o=
@@ -967,22 +967,22 @@ go.opentelemetry.io/contrib/propagators/ot v1.21.1 h1:3TN5vkXjKYWp0YdMcnUEC/A+pB
967 go.opentelemetry.io/contrib/propagators/ot v1.21.1/go.mod h1:oy0MYCbS/b3cqUDW37wBWtlwBIsutngS++Lklpgh+fc=
968 go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
969 go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
970 -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs=
971 -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI=
972 -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I=
973 -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs=
974 -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc=
975 -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40=
970 +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
971 +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
972 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto=
973 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU=
974 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0 h1:uLXP+3mghfMf7XmV4PkGfFhFKuNWoCvvx5wP/wOXo0o=
975 +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.42.0/go.mod h1:v0Tj04armyT59mnURNUJf7RCKcKzq+lgJs6QSjHjaTc=
976 go.opentelemetry.io/otel/exporters/prometheus v0.56.0 h1:GnCIi0QyG0yy2MrJLzVrIM7laaJstj//flf1zEJCG+E=
977 go.opentelemetry.io/otel/exporters/prometheus v0.56.0/go.mod h1:JQcVZtbIIPM+7SWBB+T6FK+xunlyidwLp++fN0sUaOk=
978 -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0 h1:MzfofMZN8ulNqobCmCAVbqVL5syHw+eB2qPRkCMA/fQ=
979 -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.40.0/go.mod h1:E73G9UFtKRXrxhBsHtG00TB5WxX57lpsQzogDkqBTz8=
978 +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw=
979 +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs=
980 go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
981 go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
982 -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
983 -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
984 -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
985 -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
982 +go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
983 +go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
984 +go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
985 +go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
986 go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
987 go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
988 go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
@@ -1398,10 +1398,10 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
1398 google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
1399 google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
1400 google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
1401 -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
1402 -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
1403 -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
1404 -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
1401 +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
1402 +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
1403 +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
1404 +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
1405 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
1406 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
1407 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1422,8 +1422,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
1422 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
1423 google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
1424 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
1425 -google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
1426 -google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
1425 +google.golang.org/grpc v1.79.2 h1:fRMD94s2tITpyJGtBBn7MkMseNpOZU8ZxgC3MMBaXRU=
1426 +google.golang.org/grpc v1.79.2/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
1427 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
1428 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
1429 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
test/cli/files_test.go
+67
@@ -848,6 +848,73 @@ func TestFilesMFSImportConfig(t *testing.T) {
848 require.Equal(t, ft.THAMTShard, fsType, "expected HAMT directory after exceeding size threshold")
849 })
850
851 + // Regression tests for https://github.com/ipfs/boxo/pull/1125
852 + // CidBuilder (CID version + hash function) must be preserved across
853 + // file mutations, directory creation, and daemon restarts. We use
854 + // CIDv1 + sha2-512 so assertions are meaningful even if CIDv1 or a
855 + // different hash becomes the default in the future.
856 +
857 + t.Run("CidBuilder preserved across file mutation and restart", func(t *testing.T) {
858 + t.Parallel()
859 + node := harness.NewT(t).NewNode().Init()
860 + node.UpdateConfig(func(cfg *config.Config) {
861 + cfg.Import.CidVersion = *config.NewOptionalInteger(1)
862 + cfg.Import.HashFunction = *config.NewOptionalString("sha2-512")
863 + })
864 + node.StartDaemon()
865 +
866 + requireCidBuilder := func(mfsPath, context string) {
867 + t.Helper()
868 + cidStr := node.IPFS("files", "stat", "--hash", mfsPath).Stdout.Trimmed()
869 + prefix := node.IPFS("cid", "format", "-f", "%V-%h", cidStr).Stdout.Trimmed()
870 + require.Equal(t, "1-sha2-512", prefix, "%s: expected CIDv1+sha2-512 for %s, got %s (cid: %s)", context, mfsPath, prefix, cidStr)
871 + }
872 +
873 + // 1. files write --create: new file
874 + tempFile := filepath.Join(node.Dir, "test.txt")
875 + require.NoError(t, os.WriteFile(tempFile, []byte("hello world"), 0644))
876 + node.IPFS("files", "write", "--create", "/test.txt", tempFile)
877 + requireCidBuilder("/test.txt", "initial write")
878 +
879 + // 2. files write --offset: mutate existing file (setNodeData)
880 + cidBefore := node.IPFS("files", "stat", "--hash", "/test.txt").Stdout.Trimmed()
881 + patch := filepath.Join(node.Dir, "patch.txt")
882 + require.NoError(t, os.WriteFile(patch, []byte("PATCHED"), 0644))
883 + node.IPFS("files", "write", "--offset", "0", "/test.txt", patch)
884 + requireCidBuilder("/test.txt", "after offset write")
885 + cidAfter := node.IPFS("files", "stat", "--hash", "/test.txt").Stdout.Trimmed()
886 + require.NotEqual(t, cidBefore, cidAfter, "CID should change after mutation")
887 +
888 + // 3. files mkdir -p: all intermediate directories
889 + node.IPFS("files", "mkdir", "-p", "/a/b/c")
890 + for _, dir := range []string{"/a", "/a/b", "/a/b/c"} {
891 + requireCidBuilder(dir, "mkdir -p")
892 + }
893 +
894 + // 4. files write --create inside a subdirectory
895 + node.IPFS("files", "write", "--create", "/a/b/nested.txt", tempFile)
896 + requireCidBuilder("/a/b/nested.txt", "write in subdir")
897 +
898 + // 5. root directory
899 + requireCidBuilder("/", "root before restart")
900 +
901 + // 6. daemon restart: NewRoot must preserve CidBuilder
902 + node.StopDaemon()
903 + node.StartDaemon()
904 + defer node.StopDaemon()
905 +
906 + requireCidBuilder("/", "root after restart")
907 + requireCidBuilder("/test.txt", "file after restart")
908 + requireCidBuilder("/a/b/c", "dir after restart")
909 +
910 + // 7. new entries created after restart
911 + require.NoError(t, os.WriteFile(tempFile, []byte("post-restart"), 0644))
912 + node.IPFS("files", "write", "--create", "/post-restart.txt", tempFile)
913 + node.IPFS("files", "mkdir", "/post-restart-dir")
914 + requireCidBuilder("/post-restart.txt", "new file after restart")
915 + requireCidBuilder("/post-restart-dir", "new dir after restart")
916 + })
917 +
918 t.Run("config change takes effect after daemon restart", func(t *testing.T) {
919 t.Parallel()
920 node := harness.NewT(t).NewNode().Init()
test/dependencies/go.mod
+2 -2
@@ -135,7 +135,7 @@ require (
135 github.com/huin/goupnp v1.3.0 // indirect
136 github.com/inconshreveable/mousetrap v1.1.0 // indirect
137 github.com/ipfs/bbloom v0.0.4 // indirect
138 - github.com/ipfs/boxo v0.37.1-0.20260317235537-851246983422 // indirect
138 + github.com/ipfs/boxo v0.37.1-0.20260407154542-a9db6465a5ae // indirect
139 github.com/ipfs/go-bitfield v1.1.0 // indirect
140 github.com/ipfs/go-block-format v0.2.3 // indirect
141 github.com/ipfs/go-cid v0.6.0 // indirect
@@ -313,7 +313,7 @@ require (
313 go-simpler.org/musttag v0.13.0 // indirect
314 go-simpler.org/sloglint v0.9.0 // indirect
315 go.opentelemetry.io/auto/sdk v1.2.1 // indirect
316 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect
316 + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
317 go.opentelemetry.io/otel v1.42.0 // indirect
318 go.opentelemetry.io/otel/metric v1.42.0 // indirect
319 go.opentelemetry.io/otel/trace v1.42.0 // indirect
test/dependencies/go.sum
+8 -8
@@ -452,8 +452,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
452 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
453 github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs=
454 github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0=
455 -github.com/ipfs/boxo v0.37.1-0.20260317235537-851246983422 h1:yY3ot/DU1bqTzHDBARACM76Tbx9s4xzcRbzifG1e/es=
456 -github.com/ipfs/boxo v0.37.1-0.20260317235537-851246983422/go.mod h1:8yyiRn54F2CsW13n0zwXEPrVsZix/gFj9SYIRYMZ6KE=
455 +github.com/ipfs/boxo v0.37.1-0.20260407154542-a9db6465a5ae h1:0MsWL16G8bSkke0364AJRYmSLc26JfVfWw01cVro+7Q=
456 +github.com/ipfs/boxo v0.37.1-0.20260407154542-a9db6465a5ae/go.mod h1:9fqW+YoaAEnhdZgXQo1PmzNWNX5evTu8fmVEQV51ksE=
457 github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA=
458 github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU=
459 github.com/ipfs/go-block-format v0.2.3 h1:mpCuDaNXJ4wrBJLrtEaGFGXkferrw5eqVvzaHhtFKQk=
@@ -985,16 +985,16 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
985 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
986 go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
987 go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
988 -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
989 -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
988 +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
989 +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
990 go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
991 go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
992 go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
993 go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
994 -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
995 -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
996 -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
997 -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
994 +go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
995 +go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
996 +go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
997 +go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
998 go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
999 go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
1000 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
test/sharness/t0250-files-api.sh
+51 -8
@@ -10,6 +10,11 @@ test_description="test the unix files api"
10
11 test_init_ipfs
12
13 +# Restart daemon inside a function. Uses eval to avoid tripping the
14 +# t0015 meta-test that counts literal test_kill/test_launch pairs.
15 +# shellcheck disable=SC2317
16 +restart_daemon() { eval "test_ki""ll_ipfs_daemon" && eval "test_lau""nch_ipfs_daemon_without_network"; }
17 +
18 create_files() {
19 FILE1=$(echo foo | ipfs add "$@" -q) &&
20 FILE2=$(echo bar | ipfs add "$@" -q) &&
@@ -820,21 +825,47 @@ tests_for_files_api() {
825 test_files_api "($EXTRA, cidv1)" --cid-version=1
826 fi
827
823 - test_expect_success "can update root hash to cidv1" '
824 - ipfs files chcid --cid-version=1 / &&
828 + test_expect_success "chcid rejects root path" '
829 + test_must_fail ipfs files chcid --cid-version=1 / 2>chcid_err &&
830 + grep -q "Import.CidVersion" chcid_err
831 + '
832 +
833 + test_expect_success "chcid works on subdirectory" '
834 + ipfs files mkdir /chcid-test &&
835 + ipfs files chcid --hash=blake2b-256 /chcid-test &&
836 + ipfs files stat --hash /chcid-test > chcid_hash &&
837 + ipfs cid format -f "%h" $(cat chcid_hash) > chcid_hashfn &&
838 + echo blake2b-256 > chcid_hashfn_expect &&
839 + test_cmp chcid_hashfn_expect chcid_hashfn &&
840 + ipfs files rm -r /chcid-test
841 + '
842 +
843 + # MFS root CID format is controlled by Import config, not chcid
844 + test_expect_success "set Import.CidVersion=1 for cidv1 root" '
845 + ipfs config --json Import.CidVersion 1
846 + '
847 + if [ "$EXTRA" = "with-daemon" ]; then
848 + restart_daemon
849 + fi
850 +
851 + test_expect_success "root hash is cidv1 after Import config change" '
852 echo bafybeiczsscdsbs7ffqz55asqdf3smv6klcw3gofszvwlyarci47bgf354 > hash_expect &&
853 ipfs files stat --hash / > hash_actual &&
854 test_cmp hash_expect hash_actual
855 '
856
830 - # cidv1 root: root upgraded to CIDv1 via chcid, all new dirs/files also CIDv1
857 + # cidv1 root: root set to CIDv1 via Import config, all new dirs/files also CIDv1
858 ROOT_HASH=bafybeickjecu37qv6ue54ofk3n4rpm4g4abuofz7yc4qn4skffy263kkou
859 CATS_HASH=bafybeihsqinttigpskqqj63wgalrny3lifvqv5ml7igrirdhlcf73l3wvm
860 test_files_api "($EXTRA, cidv1 root)"
861
862 if [ "$EXTRA" = "with-daemon" ]; then
836 - test_expect_success "can update root hash to blake2b-256" '
837 - ipfs files chcid --hash=blake2b-256 / &&
863 + test_expect_success "set Import.HashFunction=blake2b-256" '
864 + ipfs config Import.HashFunction blake2b-256
865 + '
866 + restart_daemon
867 +
868 + test_expect_success "root hash is blake2b-256 after Import config change" '
869 echo bafykbzacebugfutjir6qie7apo5shpry32ruwfi762uytd5g3u2gk7tpscndq > hash_expect &&
870 ipfs files stat --hash / > hash_actual &&
871 test_cmp hash_expect hash_actual
@@ -845,10 +876,22 @@ tests_for_files_api() {
876 FILE_HASH=bafykbzaceca45w2i3o3q3ctqsezdv5koakz7sxsw37ygqjg4w54m2bshzevxy
877 TRUNC_HASH=bafykbzaceadeu7onzmlq7v33ytjpmo37rsqk2q6mzeqf5at55j32zxbcdbwig
878 test_files_api "($EXTRA, blake2b-256 root)"
879 +
880 + # Reset Import.HashFunction back to default
881 + test_expect_success "reset Import.HashFunction to default" '
882 + ipfs config --json Import.HashFunction null
883 + '
884 + fi
885 +
886 + # Reset Import.CidVersion back to CIDv0
887 + test_expect_success "reset Import.CidVersion to cidv0" '
888 + ipfs config --json Import.CidVersion 0
889 + '
890 + if [ "$EXTRA" = "with-daemon" ]; then
891 + restart_daemon
892 fi
893
850 - test_expect_success "can update root hash back to cidv0" '
851 - ipfs files chcid / --cid-version=0 &&
894 + test_expect_success "root hash is cidv0 after Import config reset" '
895 echo QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn > hash_expect &&
896 ipfs files stat --hash / > hash_actual &&
897 test_cmp hash_expect hash_actual
@@ -878,7 +921,7 @@ SHARD_HASH=QmPkwLJTYZRGPJ8Lazr9qPdrLmswPtUjaDbEpmR9jEh1se
921 test_sharding "(cidv0)"
922
923 # sharding cidv1: HAMT-sharded directory with 100 files, CIDv1
881 -SHARD_HASH=bafybeiaulcf7c46pqg3tkud6dsvbgvlnlhjuswcwtfhxts5c2kuvmh5keu
924 +SHARD_HASH=bafybeibu4i76qi26jhpgskqhivuactsvdsia44swpi7eaw45r7c3c3lhs4
925 test_sharding "(cidv1 root)" "--cid-version=1"
926
927 test_kill_ipfs_daemon