fix(fuse): ipns error handling and friendly errors (#10807)
* fix(fusei/ux): check if paths exist, print err * fix(fuse): ipns 'could not resolve' error type changed when code got extracted to boxo, but it was not caught because of FUSE tests do not cover IPNS in online mode Closes #8095 Closes #2167 Closes #3013 * docs: clarify opt-in (cherry picked from commit f84fb2849bb64df667dd65c1912a0e691001ea06)
Marcin Rataj committed
May 15, 2025 at 23:43 UTC
8bdbcbf9c2af56862218c4e58eb78507eca32dcf
3 files changed
+35
-2
cmd/ipfs/kubo/daemon.go
+29
@@ -1065,16 +1065,25 @@ func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error {
1065
if !found {
1066
fsdir = cfg.Mounts.IPFS
1067
}
1068
+ if err := checkFusePath("Mounts.IPFS", fsdir); err != nil {
1069
+ return err
1070
+ }
1071
1072
nsdir, found := req.Options[ipnsMountKwd].(string)
1073
if !found {
1074
nsdir = cfg.Mounts.IPNS
1075
}
1076
+ if err := checkFusePath("Mounts.IPNS", nsdir); err != nil {
1077
+ return err
1078
+ }
1079
1080
mfsdir, found := req.Options[mfsMountKwd].(string)
1081
if !found {
1082
mfsdir = cfg.Mounts.MFS
1083
}
1084
+ if err := checkFusePath("Mounts.MFS", mfsdir); err != nil {
1085
+ return err
1086
+ }
1087
1088
node, err := cctx.ConstructNode()
1089
if err != nil {
@@ -1091,6 +1100,26 @@ func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error {
1100
return nil
1101
}
1102
1103
+func checkFusePath(name, path string) error {
1104
+ if path == "" {
1105
+ return fmt.Errorf("%s path cannot be empty", name)
1106
+ }
1107
+
1108
+ fileInfo, err := os.Stat(path)
1109
+ if err != nil {
1110
+ if os.IsNotExist(err) {
1111
+ return fmt.Errorf("%s path (%q) does not exist: %w", name, path, err)
1112
+ }
1113
+ return fmt.Errorf("error while inspecting %s path (%q): %w", name, path, err)
1114
+ }
1115
+
1116
+ if !fileInfo.IsDir() {
1117
+ return fmt.Errorf("%s path (%q) is not a directory", name, path)
1118
+ }
1119
+
1120
+ return nil
1121
+}
1122
+
1123
func maybeRunGC(req *cmds.Request, node *core.IpfsNode) (<-chan error, error) {
1124
enableGC, _ := req.Options[enableGCKwd].(bool)
1125
if !enableGC {
docs/config.md
+4
-1
@@ -1373,7 +1373,10 @@ Default: `cache`
1373
## `Mounts`
1374
1375
> [!CAUTION]
1376
-> **EXPERIMENTAL:** read about current limitations at [fuse.md](./fuse.md).
1376
+> **EXPERIMENTAL:**
1377
+> This feature is disabled by default, requires an explicit opt-in with `ipfs mount` or `ipfs daemon --mount`.
1378
+>
1379
+> Read about current limitations at [fuse.md](./fuse.md).
1380
1381
FUSE mount point configuration options.
1382
fuse/ipns/ipns_unix.go
+2
-1
@@ -16,6 +16,7 @@ import (
16
17
dag "github.com/ipfs/boxo/ipld/merkledag"
18
ft "github.com/ipfs/boxo/ipld/unixfs"
19
+ "github.com/ipfs/boxo/namesys"
20
"github.com/ipfs/boxo/path"
21
22
fuse "bazil.org/fuse"
@@ -95,7 +96,7 @@ func loadRoot(ctx context.Context, ipfs iface.CoreAPI, key iface.Key) (*mfs.Root
96
node, err := ipfs.ResolveNode(ctx, key.Path())
97
switch err {
98
case nil:
98
- case iface.ErrResolveFailed:
99
+ case namesys.ErrResolveFailed:
100
node = ft.EmptyDirNode()
101
default:
102
log.Errorf("looking up %s: %s", key.Path(), err)