Skip to content

Commit 6657f11

Browse files
committed
Add docs, add xattr listing, fix bugs for mv and stat, refactor.
1 parent 3a1bf22 commit 6657f11

File tree

3 files changed

+90
-28
lines changed

3 files changed

+90
-28
lines changed

docs/fuse.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,25 @@ ipfs config --json Mounts.FuseAllowOther true
107107
ipfs daemon --mount
108108
```
109109

110+
## MFS mountpoint
111+
112+
Since kubo release v0.35.0, it supports mounting the MFS(Mutable File System)
113+
root as a FUSE filesystem at `/mfs`, which enables you to manipulate
114+
content-addressed data like regular files. The CID of a file/directory is
115+
retrievable via the `ipfs_cid` 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+
110129
## Troubleshooting
111130

112131
#### `Permission denied` or `fusermount: user has no write access to mountpoint` error in Linux

fuse/mfs/mfs_test.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -312,11 +312,21 @@ func TestMFSRootXattr(t *testing.T) {
312312

313313
root := node.(*Dir)
314314

315-
req := fuse.GetxattrRequest{
315+
listReq := fuse.ListxattrRequest{}
316+
listRes := fuse.ListxattrResponse{}
317+
err = root.Listxattr(context.Background(), &listReq, &listRes)
318+
if err != nil {
319+
t.Fatal(err)
320+
}
321+
if slices.Compare(listRes.Xattr, []byte("ipfs_cid\x00")) != 0 {
322+
t.Fatal("list xattr returns invalid value")
323+
}
324+
325+
getReq := fuse.GetxattrRequest{
316326
Name: "ipfs_cid",
317327
}
318-
res := fuse.GetxattrResponse{}
319-
err = root.Getxattr(context.Background(), &req, &res)
328+
getRes := fuse.GetxattrResponse{}
329+
err = root.Getxattr(context.Background(), &getReq, &getRes)
320330
if err != nil {
321331
t.Fatal(err)
322332
}
@@ -326,7 +336,7 @@ func TestMFSRootXattr(t *testing.T) {
326336
t.Fatal(err)
327337
}
328338

329-
if slices.Compare(res.Xattr, []byte(ipldNode.Cid().String())) != 0 {
339+
if slices.Compare(getRes.Xattr, []byte(ipldNode.Cid().String())) != 0 {
330340
t.Fatal("xattr cid not equal to mfs root cid")
331341
}
332342
}

fuse/mfs/mfs_unix.go

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ import (
2121
"github.com/ipfs/kubo/core"
2222
)
2323

24+
const (
25+
ipfsCIDXattr = "ipfs_cid"
26+
mfsDirMode = os.ModeDir | 0755
27+
mfsFileMode = 0644
28+
blockSize = 512
29+
dirSize = 8
30+
)
31+
2432
// FUSE filesystem mounted at /mfs.
2533
type FileSystem struct {
2634
root Dir
@@ -38,18 +46,23 @@ type Dir struct {
3846

3947
// Directory attributes (stat).
4048
func (dir *Dir) Attr(ctx context.Context, attr *fuse.Attr) error {
41-
attr.Mode = os.FileMode(os.ModeDir | 0755)
42-
attr.Size = 4096
43-
attr.Blocks = 8
49+
attr.Mode = mfsDirMode
50+
attr.Size = dirSize * blockSize
51+
attr.Blocks = dirSize
4452
return nil
4553
}
4654

4755
// Access files in a directory.
4856
func (dir *Dir) Lookup(ctx context.Context, req *fuse.LookupRequest, resp *fuse.LookupResponse) (fs.Node, error) {
4957
mfsNode, err := dir.mfsDir.Child(req.Name)
50-
if err != nil {
58+
switch err {
59+
case os.ErrNotExist:
5160
return nil, syscall.Errno(syscall.ENOENT)
61+
case nil:
62+
default:
63+
return nil, err
5264
}
65+
5366
switch mfsNode.Type() {
5467
case mfs.TDir:
5568
result := Dir{
@@ -75,8 +88,12 @@ func (dir *Dir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
7588
}
7689

7790
for _, node := range nodes {
91+
nodeType := fuse.DT_File
92+
if node.Type == 1 {
93+
nodeType = fuse.DT_Dir
94+
}
7895
res = append(res, fuse.Dirent{
79-
Type: fuse.DT_File,
96+
Type: nodeType,
8097
Name: node.Name,
8198
})
8299
}
@@ -131,12 +148,23 @@ func (dir *Dir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.N
131148
}
132149
targetDir := newDir.(*Dir)
133150

151+
// Remove file if exists
152+
err = targetDir.mfsDir.Unlink(req.NewName)
153+
if err != nil && err != os.ErrNotExist {
154+
return err
155+
}
156+
134157
err = targetDir.mfsDir.AddChild(req.NewName, node)
135158
if err != nil {
136159
return err
137160
}
138161

139-
return dir.mfsDir.Unlink(req.OldName)
162+
err = dir.mfsDir.Unlink(req.OldName)
163+
if err != nil {
164+
return err
165+
}
166+
167+
return dir.mfsDir.Flush()
140168
}
141169

142170
// Create (touch) an MFS file.
@@ -187,10 +215,16 @@ func (dir *Dir) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.
187215
return &file, &handler, nil
188216
}
189217

218+
// List dir xattr.
219+
func (dir *Dir) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
220+
resp.Append(ipfsCIDXattr)
221+
return nil
222+
}
223+
190224
// Get dir xattr.
191225
func (dir *Dir) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error {
192226
switch req.Name {
193-
case "ipfs_cid":
227+
case ipfsCIDXattr:
194228
node, err := dir.mfsDir.GetNode()
195229
if err != nil {
196230
return err
@@ -209,28 +243,19 @@ type File struct {
209243

210244
// File attributes.
211245
func (file *File) Attr(ctx context.Context, attr *fuse.Attr) error {
212-
size, err := file.mfsFile.Size()
213-
if err != nil {
214-
return err
215-
}
246+
size, _ := file.mfsFile.Size()
247+
216248
attr.Size = uint64(size)
217-
if size%512 == 0 {
218-
attr.Blocks = uint64(size / 512)
249+
if size%blockSize == 0 {
250+
attr.Blocks = uint64(size / blockSize)
219251
} else {
220-
attr.Blocks = uint64(size/512 + 1)
252+
attr.Blocks = uint64(size/blockSize + 1)
221253
}
222254

223-
mtime, err := file.mfsFile.ModTime()
224-
if err != nil {
225-
return err
226-
}
255+
mtime, _ := file.mfsFile.ModTime()
227256
attr.Mtime = mtime
228257

229-
mode, err := file.mfsFile.Mode()
230-
if err != nil {
231-
return err
232-
}
233-
attr.Mode = mode
258+
attr.Mode = mfsFileMode
234259
return nil
235260
}
236261

@@ -263,10 +288,16 @@ func (file *File) Fsync(ctx context.Context, req *fuse.FsyncRequest) error {
263288
return file.mfsFile.Sync()
264289
}
265290

291+
// List file xattr.
292+
func (file *File) Listxattr(ctx context.Context, req *fuse.ListxattrRequest, resp *fuse.ListxattrResponse) error {
293+
resp.Append(ipfsCIDXattr)
294+
return nil
295+
}
296+
266297
// Get file xattr.
267298
func (file *File) Getxattr(ctx context.Context, req *fuse.GetxattrRequest, resp *fuse.GetxattrResponse) error {
268299
switch req.Name {
269-
case "ipfs_cid":
300+
case ipfsCIDXattr:
270301
node, err := file.mfsFile.GetNode()
271302
if err != nil {
272303
return err
@@ -351,6 +382,7 @@ func NewFileSystem(ipfs *core.IpfsNode) fs.FS {
351382
type mfsDir interface {
352383
fs.Node
353384
fs.NodeGetxattrer
385+
fs.NodeListxattrer
354386
fs.HandleReadDirAller
355387
fs.NodeRequestLookuper
356388
fs.NodeMkdirer
@@ -364,6 +396,7 @@ var _ mfsDir = (*Dir)(nil)
364396
type mfsFile interface {
365397
fs.Node
366398
fs.NodeGetxattrer
399+
fs.NodeListxattrer
367400
fs.NodeOpener
368401
fs.NodeFsyncer
369402
}

0 commit comments

Comments
 (0)