Skip to content

Commit 7c844ba

Browse files
gsergey418guillaumemichellidel
authored
feat(fuse): Expose MFS as FUSE mount point (#10781)
* Add MFS command line options, extend existing mount functions for MFS, set defaults. * Directory listing and file stat. * Add a read-only MFS view. * Add mkdir and interface checks. * Add remove and rename functionality. * Implement all required write interfaces. * Adjust mount functions for other architechtures. * Merge branch 'master' into feat/10710-mfs-fuse-mount * Write a basic read/write test. * Write more basic tests, add a mutex to the file object, fix modtime. * Add a concurrency test, remove mutexes from file and directory structures. * Refactor naming(mfdir -> mfsdir) and add documentation. * Add CID retrieval through ipfs_cid xattr. * Add docs, add xattr listing, fix bugs for mv and stat, refactor. * Add MFS command line options, extend existing mount functions for MFS, set defaults. * docs phrasing * docs: Mounts.MFS * docs: warn about lazy-loaded DAGs * test: TEST_FUSE=1 ./t0030-mount.sh -v --------- Co-authored-by: Guillaume Michel <guillaumemichel@users.noreply.github.com> Co-authored-by: guillaumemichel <guillaume@michel.id> Co-authored-by: Marcin Rataj <lidel@lidel.org>
1 parent b5d7369 commit 7c844ba

File tree

26 files changed

+962
-42
lines changed

26 files changed

+962
-42
lines changed

cmd/ipfs/kubo/daemon.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const (
5656
initProfileOptionKwd = "init-profile"
5757
ipfsMountKwd = "mount-ipfs"
5858
ipnsMountKwd = "mount-ipns"
59+
mfsMountKwd = "mount-mfs"
5960
migrateKwd = "migrate"
6061
mountKwd = "mount"
6162
offlineKwd = "offline" // global option
@@ -173,6 +174,7 @@ Headers.
173174
cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem using FUSE (experimental)"),
174175
cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount). Defaults to config setting."),
175176
cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount). Defaults to config setting."),
177+
cmds.StringOption(mfsMountKwd, "Path to the mountpoint for MFS (if using --mount). Defaults to config setting."),
176178
cmds.BoolOption(unrestrictedAPIAccessKwd, "Allow RPC API access to unlisted hashes"),
177179
cmds.BoolOption(unencryptTransportKwd, "Disable transport encryption (for debugging protocols)"),
178180
cmds.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection"),
@@ -1062,17 +1064,23 @@ func mountFuse(req *cmds.Request, cctx *oldcmds.Context) error {
10621064
nsdir = cfg.Mounts.IPNS
10631065
}
10641066

1067+
mfsdir, found := req.Options[mfsMountKwd].(string)
1068+
if !found {
1069+
mfsdir = cfg.Mounts.MFS
1070+
}
1071+
10651072
node, err := cctx.ConstructNode()
10661073
if err != nil {
10671074
return fmt.Errorf("mountFuse: ConstructNode() failed: %s", err)
10681075
}
10691076

1070-
err = nodeMount.Mount(node, fsdir, nsdir)
1077+
err = nodeMount.Mount(node, fsdir, nsdir, mfsdir)
10711078
if err != nil {
10721079
return err
10731080
}
10741081
fmt.Printf("IPFS mounted at: %s\n", fsdir)
10751082
fmt.Printf("IPNS mounted at: %s\n", nsdir)
1083+
fmt.Printf("MFS mounted at: %s\n", mfsdir)
10761084
return nil
10771085
}
10781086

config/init.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ func InitWithIdentity(identity Identity) (*Config, error) {
5252
Mounts: Mounts{
5353
IPFS: "/ipfs",
5454
IPNS: "/ipns",
55+
MFS: "/mfs",
5556
},
5657

5758
Ipns: Ipns{

config/mounts.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ package config
44
type Mounts struct {
55
IPFS string
66
IPNS string
7+
MFS string
78
FuseAllowOther bool
89
}

core/commands/mount_nofuse.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ var MountCmd = &cmds.Command{
1414
ShortDescription: `
1515
This version of ipfs is compiled without fuse support, which is required
1616
for mounting. If you'd like to be able to mount, please use a version of
17-
ipfs compiled with fuse.
17+
Kubo compiled with fuse.
1818
1919
For the latest instructions, please check the project's repository:
20-
https://github.com/ipfs/go-ipfs
20+
https://github.com/ipfs/kubo
21+
https://github.com/ipfs/kubo/blob/master/docs/fuse.md
2122
`,
2223
},
2324
}

core/commands/mount_unix.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,22 @@ import (
1818
const (
1919
mountIPFSPathOptionName = "ipfs-path"
2020
mountIPNSPathOptionName = "ipns-path"
21+
mountMFSPathOptionName = "mfs-path"
2122
)
2223

2324
var MountCmd = &cmds.Command{
2425
Status: cmds.Experimental,
2526
Helptext: cmds.HelpText{
2627
Tagline: "Mounts IPFS to the filesystem (read-only).",
2728
ShortDescription: `
28-
Mount IPFS at a read-only mountpoint on the OS (default: /ipfs and /ipns).
29+
Mount IPFS at a read-only mountpoint on the OS (default: /ipfs, /ipns, /mfs).
2930
All IPFS objects will be accessible under that directory. Note that the
3031
root will not be listable, as it is virtual. Access known paths directly.
3132
3233
You may have to create /ipfs and /ipns before using 'ipfs mount':
3334
34-
> sudo mkdir /ipfs /ipns
35-
> sudo chown $(whoami) /ipfs /ipns
35+
> sudo mkdir /ipfs /ipns /mfs
36+
> sudo chown $(whoami) /ipfs /ipns /mfs
3637
> ipfs daemon &
3738
> ipfs mount
3839
`,
@@ -44,8 +45,8 @@ root will not be listable, as it is virtual. Access known paths directly.
4445
4546
You may have to create /ipfs and /ipns before using 'ipfs mount':
4647
47-
> sudo mkdir /ipfs /ipns
48-
> sudo chown $(whoami) /ipfs /ipns
48+
> sudo mkdir /ipfs /ipns /mfs
49+
> sudo chown $(whoami) /ipfs /ipns /mfs
4950
> ipfs daemon &
5051
> ipfs mount
5152
@@ -67,6 +68,7 @@ baz
6768
> ipfs mount
6869
IPFS mounted at: /ipfs
6970
IPNS mounted at: /ipns
71+
MFS mounted at: /mfs
7072
> cd /ipfs/QmSh5e7S6fdcu75LAbXNZAFY2nGyZUJXyLCJDvn2zRkWyC
7173
> ls
7274
bar
@@ -81,6 +83,7 @@ baz
8183
Options: []cmds.Option{
8284
cmds.StringOption(mountIPFSPathOptionName, "f", "The path where IPFS should be mounted."),
8385
cmds.StringOption(mountIPNSPathOptionName, "n", "The path where IPNS should be mounted."),
86+
cmds.StringOption(mountMFSPathOptionName, "m", "The path where MFS should be mounted."),
8487
},
8588
Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
8689
cfg, err := env.(*oldcmds.Context).GetConfig()
@@ -109,21 +112,28 @@ baz
109112
nsdir = cfg.Mounts.IPNS // NB: be sure to not redeclare!
110113
}
111114

112-
err = nodeMount.Mount(nd, fsdir, nsdir)
115+
mfsdir, found := req.Options[mountMFSPathOptionName].(string)
116+
if !found {
117+
mfsdir = cfg.Mounts.MFS
118+
}
119+
120+
err = nodeMount.Mount(nd, fsdir, nsdir, mfsdir)
113121
if err != nil {
114122
return err
115123
}
116124

117125
var output config.Mounts
118126
output.IPFS = fsdir
119127
output.IPNS = nsdir
128+
output.MFS = mfsdir
120129
return cmds.EmitOnce(res, &output)
121130
},
122131
Type: config.Mounts{},
123132
Encoders: cmds.EncoderMap{
124133
cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, mounts *config.Mounts) error {
125134
fmt.Fprintf(w, "IPFS mounted at: %s\n", cmdenv.EscNonPrint(mounts.IPFS))
126135
fmt.Fprintf(w, "IPNS mounted at: %s\n", cmdenv.EscNonPrint(mounts.IPNS))
136+
fmt.Fprintf(w, "MFS mounted at: %s\n", cmdenv.EscNonPrint(mounts.MFS))
127137

128138
return nil
129139
}),

core/core.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ type IpfsNode struct {
134134
type Mounts struct {
135135
Ipfs mount.Mount
136136
Ipns mount.Mount
137+
Mfs mount.Mount
137138
}
138139

139140
// Close calls Close() on the App object

docs/changelogs/v0.35.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ This release was brought to you by the [Shipyard](http://ipshipyard.com/) team.
1212
- [🔦 Highlights](#-highlights)
1313
- [Opt-in HTTP Retrieval client](#opt-in-http-retrieval-client)
1414
- [Dedicated `Reprovider.Strategy` for MFS](#dedicated-reproviderstrategy-for-mfs)
15+
- [Experimental support for MFS as a FUSE mount point](#experimental-support-for-mfs-as-a-fuse-mount-point)
1516
- [Grid view in WebUI](#grid-view-in-webui)
1617
- [Enhanced DAG-Shaping Controls](#enhanced-dag-shaping-controls)
1718
- [New DAG-Shaping `ipfs add` Options](#new-dag-shaping-ipfs-add-options)
@@ -64,6 +65,14 @@ Users relying on the `pinned` strategy can switch to `pinned+mfs` and use MFS al
6465

6566
See [`Reprovider.Strategy`](https://github.com/ipfs/kubo/blob/master/docs/config.md#reproviderstrategy) for more details.
6667

68+
#### Experimental support for MFS as a FUSE mount point
69+
70+
The MFS root (filesystem behind the `ipfs files` API) is now available as a read/write FUSE mount point at `Mounts.MFS`. This filesystem is mounted in the same way as `Mounts.IPFS` and `Mounts.IPNS` when running `ipfs mount` or `ipfs daemon --mount`.
71+
72+
Note that the operations supported by the MFS FUSE mountpoint are limited, since MFS doesn't store file attributes.
73+
74+
See [`Mounts`](https://github.com/ipfs/kubo/blob/master/docs/config.md#mounts) and [`docs/fuse.md`](https://github.com/ipfs/kubo/blob/master/docs/fuse.md) for more details.
75+
6776
#### Grid view in WebUI
6877

6978
The WebUI, accessible at http://127.0.0.1:5001/webui/, now includes support for the grid view on the _Files_ screen:

docs/config.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ config file at runtime.
9797
- [`Mounts`](#mounts)
9898
- [`Mounts.IPFS`](#mountsipfs)
9999
- [`Mounts.IPNS`](#mountsipns)
100+
- [`Mounts.MFS`](#mountsmfs)
100101
- [`Mounts.FuseAllowOther`](#mountsfuseallowother)
101102
- [`Pinning`](#pinning)
102103
- [`Pinning.RemoteServices`](#pinningremoteservices)
@@ -1368,7 +1369,8 @@ Default: `cache`
13681369

13691370
## `Mounts`
13701371

1371-
**EXPERIMENTAL:** read about current limitations at [fuse.md](./fuse.md).
1372+
> [!CAUTION]
1373+
> **EXPERIMENTAL:** read about current limitations at [fuse.md](./fuse.md).
13721374
13731375
FUSE mount point configuration options.
13741376

@@ -1388,6 +1390,18 @@ Default: `/ipns`
13881390

13891391
Type: `string` (filesystem path)
13901392

1393+
### `Mounts.MFS`
1394+
1395+
Mountpoint for Mutable File System (MFS) behind the `ipfs files` API.
1396+
1397+
> [!CAUTION]
1398+
> - Write support is highly experimental and not recommended for mission-critical deployments.
1399+
> - Avoid storing lazy-loaded datasets in MFS. Exposing a partially local, lazy-loaded DAG risks operating system search indexers crawling it, which may trigger unintended network prefetching of non-local DAG components.
1400+
1401+
Default: `/mfs`
1402+
1403+
Type: `string` (filesystem path)
1404+
13911405
### `Mounts.FuseAllowOther`
13921406

13931407
Sets the 'FUSE allow other'-option on the mount point.

docs/experimental-features.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ We also support the use of protocol names of the form /x/$NAME/http where $NAME
404404

405405
## FUSE
406406

407-
FUSE makes it possible to mount `/ipfs` and `/ipns` namespaces in your OS,
407+
FUSE makes it possible to mount `/ipfs`, `/ipns` and `/mfs` namespaces in your OS,
408408
allowing arbitrary apps access to IPFS using a subset of filesystem abstractions.
409409

410410
It is considered EXPERIMENTAL due to limited (and buggy) support on some platforms.

docs/fuse.md

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**EXPERIMENTAL:** FUSE support is limited, YMMV.
44

5-
Kubo makes it possible to mount `/ipfs` and `/ipns` namespaces in your OS,
5+
Kubo makes it possible to mount `/ipfs`, `/ipns` and `/mfs` namespaces in your OS,
66
allowing arbitrary apps access to IPFS.
77

88
## Install FUSE
@@ -50,18 +50,20 @@ speak with us, or if you figure something new out, please add to this document!
5050

5151
## Prepare mountpoints
5252

53-
By default ipfs uses `/ipfs` and `/ipns` directories for mounting, this can be
54-
changed in config. You will have to create the `/ipfs` and `/ipns` directories
53+
By default ipfs uses `/ipfs`, `/ipns` and `/mfs` directories for mounting, this can be
54+
changed in config. You will have to create the `/ipfs`, `/ipns` and `/mfs` directories
5555
explicitly. Note that modifying root requires sudo permissions.
5656

5757
```sh
5858
# make the directories
5959
sudo mkdir /ipfs
6060
sudo mkdir /ipns
61+
sudo mkdir /mfs
6162

6263
# chown them so ipfs can use them without root permissions
6364
sudo chown <username> /ipfs
6465
sudo chown <username> /ipns
66+
sudo chown <username> /mfs
6567
```
6668

6769
Depending on whether you are using OSX or Linux, follow the proceeding instructions.
@@ -105,6 +107,25 @@ ipfs config --json Mounts.FuseAllowOther true
105107
ipfs daemon --mount
106108
```
107109

110+
## MFS mountpoint
111+
112+
Kubo v0.35.0 and later supports mounting the MFS (Mutable File System) root as
113+
a FUSE filesystem, enabling manipulation of content-addressed data like regular
114+
files. The CID for any file or directory is retrievable via the `ipfs_cid`
115+
extended attribute.
116+
117+
```sh
118+
getfattr -n ipfs_cid /mfs/welcome-to-IPFS.jpg
119+
getfattr: Removing leading '/' from absolute path names
120+
# file: mfs/welcome-to-IPFS.jpg
121+
ipfs_cid="QmaeXDdwpUeKQcMy7d5SFBfVB4y7LtREbhm5KizawPsBSH"
122+
```
123+
124+
Please note that the operations supported by the MFS FUSE mountpoint are
125+
limited. Since the MFS wasn't designed to store file attributes like ownership
126+
information, permissions and creation date, some applications like `vim` and
127+
`sed` may misbehave due to missing functionality.
128+
108129
## Troubleshooting
109130

110131
#### `Permission denied` or `fusermount: user has no write access to mountpoint` error in Linux
@@ -145,6 +166,7 @@ set for user running `ipfs mount` command.
145166
```
146167
sudo umount /ipfs
147168
sudo umount /ipns
169+
sudo umount /mfs
148170
```
149171

150172
#### Mounting fails with "error mounting: could not resolve name"

0 commit comments

Comments
 (0)