fuse: misc fixes

6 views
Skip to first unread message

nor...@perkeep.org

unread,
Jul 1, 2022, 10:40:52 AM7/1/22
to camlistor...@googlegroups.com


https://github.com/perkeep/perkeep/commit/215de258f2c482b6b391bc4b57d973f2dd6a93bc

commit 215de258f2c482b6b391bc4b57d973f2dd6a93bc
Author: Michael Hoffmann <mho...@posteo.de>
Date: Mon Jun 20 23:04:57 2022 +0200

fuse: misc fixes

* propagate more context cancelations from pkg/client
* convert ErrCancel to fuse.EINTR to enable clients to handle interrupts
* improve interrupt handling in fs_test.go
* fix race in mutDir between rename and first populate

diff --git a/cmd/pk-mount/pkmount.go b/cmd/pk-mount/pkmount.go
index 4eba349..2a708b3 100644
--- a/cmd/pk-mount/pkmount.go
+++ b/cmd/pk-mount/pkmount.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
diff --git a/cmd/pk-mount/pkmount_other.go b/cmd/pk-mount/pkmount_other.go
index 7da4764..f6dc768 100644
--- a/cmd/pk-mount/pkmount_other.go
+++ b/cmd/pk-mount/pkmount_other.go
@@ -1,3 +1,4 @@
+//go:build !linux && !darwin
// +build !linux,!darwin

/*
diff --git a/pkg/client/get.go b/pkg/client/get.go
index 441d4ea..2744ab4 100644
--- a/pkg/client/get.go
+++ b/pkg/client/get.go
@@ -135,7 +135,7 @@ func (c *Client) fetchVia(ctx context.Context, b blob.Ref, v []blob.Ref) (body i
size = uint32(n)
reader, closer = &buf, types.NopCloser
} else {
- return nil, 0, fmt.Errorf("Error reading %s: %v", b, err)
+ return nil, 0, fmt.Errorf("Error reading %s: %w", b, err)
}
}

diff --git a/pkg/client/upload.go b/pkg/client/upload.go
index b084588..5f35834 100644
--- a/pkg/client/upload.go
+++ b/pkg/client/upload.go
@@ -212,7 +212,7 @@ func (c *Client) doStat(ctx context.Context, blobs []blob.Ref, wait time.Duratio
resp, err = c.httpClient.Do(req)
}
if err != nil {
- return fmt.Errorf("stat HTTP error: %v", err)
+ return fmt.Errorf("stat HTTP error: %w", err)
}
if resp.Body != nil {
defer resp.Body.Close()
@@ -303,7 +303,7 @@ func (c *Client) Upload(ctx context.Context, h *UploadHandle) (*PutResult, error

resp, err := c.doReqGated(req)
if err != nil {
- return errorf("stat http error: %v", err)
+ return errorf("stat http error: %w", err)
}
defer resp.Body.Close()

@@ -368,13 +368,13 @@ func (c *Client) Upload(ctx context.Context, h *UploadHandle) (*PutResult, error
req.ContentLength = getMultipartOverhead() + bodySize + int64(len(blobrefStr))*2
resp, err := c.doReqGated(req)
if err != nil {
- return errorf("upload http error: %v", err)
+ return errorf("upload http error: %w", err)
}
defer resp.Body.Close()

// check error from earlier copy
if err := <-copyResult; err != nil {
- return errorf("failed to copy contents into multipart writer: %v", err)
+ return errorf("failed to copy contents into multipart writer: %w", err)
}

// The only valid HTTP responses are 200 and 303.
@@ -390,18 +390,18 @@ func (c *Client) Upload(ctx context.Context, h *UploadHandle) (*PutResult, error
baseURL, _ := url.Parse(uploadURL)
absURL, err := baseURL.Parse(otherLocation)
if err != nil {
- return errorf("303 Location URL relative resolve error: %v", err)
+ return errorf("303 Location URL relative resolve error: %w", err)
}
otherLocation = absURL.String()
resp, err = http.Get(otherLocation)
if err != nil {
- return errorf("error following 303 redirect after upload: %v", err)
+ return errorf("error following 303 redirect after upload: %w", err)
}
}

var ures protocol.UploadResponse
if err := httputil.DecodeJSON(resp, &ures); err != nil {
- return errorf("error in upload response: %v", err)
+ return errorf("error in upload response: %w", err)
}

if ures.ErrorText != "" {
diff --git a/pkg/fs/at.go b/pkg/fs/at.go
index 50e6534..fb9b72e 100644
--- a/pkg/fs/at.go
+++ b/pkg/fs/at.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
diff --git a/pkg/fs/debug.go b/pkg/fs/debug.go
index 967a6b1..beba8aa 100644
--- a/pkg/fs/debug.go
+++ b/pkg/fs/debug.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go
index 9e34df5..f631c87 100644
--- a/pkg/fs/fs.go
+++ b/pkg/fs/fs.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -120,8 +121,8 @@ type node struct {
var _ fusefs.Node = (*node)(nil)

func (n *node) Attr(ctx context.Context, a *fuse.Attr) error {
- if _, err := n.schema(); err != nil {
- return err
+ if _, err := n.schema(ctx); err != nil {
+ return handleEIOorEINTR(err)
}
*a = n.attr
return nil
@@ -162,14 +163,13 @@ func (n *node) Lookup(ctx context.Context, name string) (fusefs.Node, error) {
return &node{fs: n.fs, blobref: ref}, nil
}

-func (n *node) schema() (*schema.Blob, error) {
- // TODO: use singleflight library here instead of a lock?
+func (n *node) schema(ctx context.Context) (*schema.Blob, error) {
n.mu.Lock()
defer n.mu.Unlock()
if n.meta != nil {
return n.meta, nil
}
- blob, err := n.fs.fetchSchemaMeta(context.TODO(), n.blobref)
+ blob, err := n.fs.fetchSchemaMeta(ctx, n.blobref)
if err == nil {
n.meta = blob
n.populateAttr()
@@ -189,10 +189,10 @@ func (n *node) Open(ctx context.Context, req *fuse.OpenRequest, res *fuse.OpenRe
if isWriteFlags(req.Flags) {
return nil, fuse.EPERM
}
- ss, err := n.schema()
+ ss, err := n.schema(ctx)
if err != nil {
Logger.Printf("open of %v: %v", n.blobref, err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
if ss.Type() == schema.TypeDirectory {
return n, nil
@@ -253,20 +253,20 @@ func (n *node) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
return n.dirents, nil
}

- ss, err := n.schema()
+ ss, err := n.schema(ctx)
if err != nil {
Logger.Printf("camli.ReadDirAll error on %v: %v", n.blobref, err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
dr, err := schema.NewDirReader(ctx, n.fs.fetcher, ss.BlobRef())
if err != nil {
Logger.Printf("camli.ReadDirAll error on %v: %v", n.blobref, err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
schemaEnts, err := dr.Readdir(ctx, -1)
if err != nil {
Logger.Printf("camli.ReadDirAll error on %v: %v", n.blobref, err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
n.dirents = make([]fuse.Dirent, 0)
for _, sent := range schemaEnts {
@@ -378,7 +378,7 @@ func (fs *CamliFileSystem) newNodeFromBlobRef(root blob.Ref) (fusefs.Node, error

case schema.TypePermanode:
// other mutDirs listed in the default filesystem have names and are displayed
- return &mutDir{fs: fs, permanode: root, name: "-"}, nil
+ return &mutDir{fs: fs, permanode: root, name: "-", children: make(map[string]mutFileOrDir)}, nil
}

return nil, fmt.Errorf("Blobref must be of a directory or permanode got a %v", blob.Type())
diff --git a/pkg/fs/fs_test.go b/pkg/fs/fs_test.go
index 3fe8c46..a1d1bc8 100644
--- a/pkg/fs/fs_test.go
+++ b/pkg/fs/fs_test.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -35,6 +36,7 @@ import (
"strconv"
"strings"
"sync"
+ "syscall"
"testing"
"time"

@@ -571,6 +573,31 @@ func parseXattrList(from []byte) map[string]bool {
}

func TestXattr(t *testing.T) {
+ setxattr := func(path, attr string, data []byte, flags int) error {
+ return ignoringEINTR(func() error {
+ return syscallx.Setxattr(path, attr, data, flags)
+ })
+ }
+ getxattr := func(path, attr string, data []byte) (sz int, err error) {
+ err = ignoringEINTR(func() error {
+ sz, err = syscallx.Getxattr(path, attr, data)
+ return err
+ })
+ return
+ }
+ listxattr := func(path string, data []byte) (sz int, err error) {
+ err = ignoringEINTR(func() error {
+ sz, err = syscallx.Listxattr(path, data)
+ return err
+ })
+ return
+ }
+ removexattr := func(path, attr string) (err error) {
+ return ignoringEINTR(func() error {
+ return syscallx.Removexattr(path, attr)
+ })
+ }
+
condSkip(t)
inEmptyMutDir(t, func(env *mountEnv, rootDir string) {
name1 := filepath.Join(rootDir, "1")
@@ -585,7 +612,7 @@ func TestXattr(t *testing.T) {

buf := make([]byte, 8192)
// list empty
- n, err := syscallx.Listxattr(name1, buf)
+ n, err := listxattr(name1, buf)
if err != nil {
t.Errorf("Error in initial listxattr: %v", err)
}
@@ -594,28 +621,28 @@ func TestXattr(t *testing.T) {
}

// get missing
- n, err = syscallx.Getxattr(name1, attr1, buf)
+ n, err = getxattr(name1, attr1, buf)
if err == nil {
t.Errorf("Expected error getting non-existent xattr, got %q", buf[:n])
}

// Set (two different attributes)
- err = syscallx.Setxattr(name1, attr1, []byte("hello1"), 0)
+ err = setxattr(name1, attr1, []byte("hello1"), 0)
if err != nil {
t.Fatalf("Error setting xattr: %v", err)
}
- err = syscallx.Setxattr(name1, attr2, []byte("hello2"), 0)
+ err = setxattr(name1, attr2, []byte("hello2"), 0)
if err != nil {
t.Fatalf("Error setting xattr: %v", err)
}
// Alternate value for first attribute
- err = syscallx.Setxattr(name1, attr1, []byte("hello1a"), 0)
+ err = setxattr(name1, attr1, []byte("hello1a"), 0)
if err != nil {
t.Fatalf("Error setting xattr: %v", err)
}

// list attrs
- n, err = syscallx.Listxattr(name1, buf)
+ n, err = listxattr(name1, buf)
if err != nil {
t.Errorf("Error in initial listxattr: %v", err)
}
@@ -625,13 +652,13 @@ func TestXattr(t *testing.T) {
}

// Remove attr
- err = syscallx.Removexattr(name1, attr2)
+ err = removexattr(name1, attr2)
if err != nil {
t.Errorf("Failed to remove attr: %v", err)
}

// List attrs
- n, err = syscallx.Listxattr(name1, buf)
+ n, err = listxattr(name1, buf)
if err != nil {
t.Errorf("Error in initial listxattr: %v", err)
}
@@ -641,7 +668,7 @@ func TestXattr(t *testing.T) {
}

// Get remaining attr
- n, err = syscallx.Getxattr(name1, attr1, buf)
+ n, err = getxattr(name1, attr1, buf)
if err != nil {
t.Errorf("Error getting attr1: %v", err)
}
@@ -894,3 +921,14 @@ func shortenString(v string) string {
flush()
return buf.String()
}
+
+// https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/os/file_posix.go;drc=635b1244aa7671bcd665613680f527452cac7555;l=243
+// some code to deal with EINTR on the application side
+func ignoringEINTR(fn func() error) error {
+ for {
+ err := fn()
+ if err != syscall.EINTR {
+ return err
+ }
+ }
+}
diff --git a/pkg/fs/mut.go b/pkg/fs/mut.go
index ca1f9de..16dac8b 100644
--- a/pkg/fs/mut.go
+++ b/pkg/fs/mut.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -123,10 +124,9 @@ func (n *mutFile) Access(ctx context.Context, req *fuse.AccessRequest) error {
}

// populate hits the blobstore to populate map of child nodes.
-func (n *mutDir) populate() error {
+func (n *mutDir) populate(ctx context.Context) error {
n.mu.Lock()
defer n.mu.Unlock()
- ctx := context.TODO()

// Only re-populate if we haven't done so recently.
now := time.Now()
@@ -140,8 +140,7 @@ func (n *mutDir) populate() error {
Depth: 3,
})
if err != nil {
- Logger.Println("mutDir.paths:", err)
- return nil
+ return err
}
db := res.Meta[n.permanode.String()]
if db == nil {
@@ -149,9 +148,6 @@ func (n *mutDir) populate() error {
}

// Find all child permanodes and stick them in n.children
- if n.children == nil {
- n.children = make(map[string]mutFileOrDir)
- }
currentChildren := map[string]bool{}
for k, v := range db.Permanode.Attr {
const p = "camliPath:"
@@ -186,6 +182,7 @@ func (n *mutDir) populate() error {
permanode: blob.ParseOrZero(childRef),
parent: n,
name: name,
+ children: make(map[string]mutFileOrDir),
})
} else if contentRef := child.Permanode.Attr.Get("camliContent"); contentRef != "" {
// This is a file.
@@ -254,9 +251,9 @@ func isDir(d *search.DescribedPermanode) bool {
}

func (n *mutDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
- if err := n.populate(); err != nil {
+ if err := n.populate(ctx); err != nil {
Logger.Println("populate:", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
n.mu.Lock()
defer n.mu.Unlock()
@@ -288,9 +285,9 @@ func (n *mutDir) Lookup(ctx context.Context, name string) (ret fs.Node, err erro
defer func() {
Logger.Printf("mutDir(%q).Lookup(%q) = %v, %v", n.fullPath(), name, ret, err)
}()
- if err := n.populate(); err != nil {
+ if err := n.populate(ctx); err != nil {
Logger.Println("populate:", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
n.mu.Lock()
defer n.mu.Unlock()
@@ -313,7 +310,7 @@ func (n *mutDir) Create(ctx context.Context, req *fuse.CreateRequest, res *fuse.
child, err := n.creat(ctx, req.Name, fileType)
if err != nil {
Logger.Printf("mutDir.Create(%q): %v", req.Name, err)
- return nil, nil, fuse.EIO
+ return nil, nil, handleEIOorEINTR(err)
}

// Create and return a file handle.
@@ -329,7 +326,7 @@ func (n *mutDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, er
child, err := n.creat(ctx, req.Name, dirType)
if err != nil {
Logger.Printf("mutDir.Mkdir(%q): %v", req.Name, err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
return child, nil
}
@@ -339,7 +336,7 @@ func (n *mutDir) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node
node, err := n.creat(ctx, req.NewName, symlinkType)
if err != nil {
Logger.Printf("mutDir.Symlink(%q): %v", req.NewName, err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
mf := node.(*mutFile)
mf.symLink = true
@@ -349,7 +346,7 @@ func (n *mutDir) Symlink(ctx context.Context, req *fuse.SymlinkRequest) (fs.Node
_, err = n.fs.client.UploadAndSignBlob(ctx, claim)
if err != nil {
Logger.Printf("mutDir.Symlink(%q) upload error: %v", req.NewName, err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}

return node, nil
@@ -407,8 +404,9 @@ func (n *mutDir) creat(ctx context.Context, name string, typ nodeType) (fs.Node,
permanode: pr.BlobRef,
parent: n,
name: name,
- xattrs: map[string][]byte{},
+ xattrs: make(map[string][]byte),
localCreateTime: time.Now(),
+ children: make(map[string]mutFileOrDir),
}
case fileType, symlinkType:
child = &mutFile{
@@ -416,16 +414,13 @@ func (n *mutDir) creat(ctx context.Context, name string, typ nodeType) (fs.Node,
permanode: pr.BlobRef,
parent: n,
name: name,
- xattrs: map[string][]byte{},
+ xattrs: make(map[string][]byte),
localCreateTime: time.Now(),
}
default:
panic("bogus creat type")
}
n.mu.Lock()
- if n.children == nil {
- n.children = make(map[string]mutFileOrDir)
- }
n.children[name] = child
n.mu.Unlock()

@@ -440,7 +435,7 @@ func (n *mutDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
_, err := n.fs.client.UploadAndSignBlob(ctx, claim)
if err != nil {
Logger.Println("mutDir.Remove:", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}
// Remove child from map.
n.mu.Lock()
@@ -464,11 +459,11 @@ func (n *mutDir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.
}

var wg syncutil.Group
- wg.Go(n.populate)
- wg.Go(n2.populate)
+ wg.Go(func() error { return n.populate(ctx) })
+ wg.Go(func() error { return n2.populate(ctx) })
if err := wg.Err(); err != nil {
Logger.Printf("*mutDir.Rename src dir populate = %v", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}

n.mu.Lock()
@@ -488,7 +483,7 @@ func (n *mutDir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.
_, err := n.fs.client.UploadAndSignBlob(ctx, claim)
if err != nil {
Logger.Printf("Upload rename link error: %v", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}

var grp syncutil.Group
@@ -509,7 +504,7 @@ func (n *mutDir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.
}
if err := grp.Err(); err != nil {
Logger.Printf("Upload rename unlink/title error: %v", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}

// TODO(bradfitz): this locking would be racy, if the kernel
@@ -677,7 +672,7 @@ func (n *mutFile) Open(ctx context.Context, req *fuse.OpenRequest, res *fuse.Ope
if err != nil {
mutFileOpenError.Incr()
Logger.Printf("mutFile.Open: %v", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}

// Read-only.
@@ -854,13 +849,13 @@ func (h *mutFileHandle) Flush(ctx context.Context, req *fuse.FlushRequest) error
br, err := schema.WriteFileFromReader(ctx, h.f.fs.client, h.f.name, &io.LimitedReader{R: h.tmp, N: h.f.size})
if err != nil {
Logger.Println("mutFileHandle.Flush:", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}
h.f.content = br
claim := schema.NewSetAttributeClaim(h.f.permanode, "camliContent", br.String())
if _, err := h.f.fs.client.UploadAndSignBlob(ctx, claim); err != nil {
Logger.Printf("mutFileHandle.Flush: %v", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}

return nil
diff --git a/pkg/fs/mut_test.go b/pkg/fs/mut_test.go
index a9888d0..c50f5df 100644
--- a/pkg/fs/mut_test.go
+++ b/pkg/fs/mut_test.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
diff --git a/pkg/fs/recent.go b/pkg/fs/recent.go
index a4207bb..35b36b9 100644
--- a/pkg/fs/recent.go
+++ b/pkg/fs/recent.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -83,7 +84,7 @@ func (n *recentDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
res, err := n.fs.client.GetRecentPermanodes(ctx, req)
if err != nil {
Logger.Printf("fs.recent: GetRecentPermanodes error in ReadDirAll: %v", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}

n.lastNames = nil
@@ -142,9 +143,16 @@ func (n *recentDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
if n.ents == nil {
// Odd case: a Lookup before a Readdir. Force a readdir to
// seed our map. Mostly hit just during development.
- n.mu.Unlock() // release, since ReadDirAll will acquire
- n.ReadDirAll(ctx)
- n.mu.Lock()
+ refresh := func() error {
+ n.mu.Unlock() // release, since ReadDirAll will acquire
+ defer n.mu.Lock()
+
+ _, err := n.ReadDirAll(ctx)
+ return err
+ }
+ if err := refresh(); err != nil {
+ return nil, err
+ }
}
db := n.ents[name]
Logger.Printf("fs.recent: Lookup(%q) = %v", name, db)
diff --git a/pkg/fs/ro.go b/pkg/fs/ro.go
index 04aa869..fe05fc9 100644
--- a/pkg/fs/ro.go
+++ b/pkg/fs/ro.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -21,6 +22,7 @@ package fs
import (
"context"
"errors"
+ "fmt"
"os"
"path/filepath"
"strings"
@@ -82,10 +84,9 @@ func (n *roDir) Attr(ctx context.Context, a *fuse.Attr) error {
}

// populate hits the blobstore to populate map of child nodes.
-func (n *roDir) populate() error {
+func (n *roDir) populate(ctx context.Context) error {
n.mu.Lock()
defer n.mu.Unlock()
- ctx := context.TODO()
// Things never change here, so if we've ever populated, we're
// populated.
if n.children != nil {
@@ -101,7 +102,7 @@ func (n *roDir) populate() error {
})
if err != nil {
Logger.Println("roDir.paths:", err)
- return nil
+ return fmt.Errorf("error while describing permanode: %w", err)
}
db := res.Meta[n.permanode.String()]
if db == nil {
@@ -170,9 +171,9 @@ func (n *roDir) populate() error {
}

func (n *roDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
- if err := n.populate(); err != nil {
+ if err := n.populate(ctx); err != nil {
Logger.Println("populate:", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
n.mu.Lock()
defer n.mu.Unlock()
@@ -206,9 +207,9 @@ func (n *roDir) Lookup(ctx context.Context, name string) (ret fs.Node, err error
defer func() {
Logger.Printf("roDir(%q).Lookup(%q) = %#v, %v", n.fullPath(), name, ret, err)
}()
- if err := n.populate(); err != nil {
+ if err := n.populate(ctx); err != nil {
Logger.Println("populate:", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
n.mu.Lock()
defer n.mu.Unlock()
@@ -346,7 +347,7 @@ func (n *roFile) Open(ctx context.Context, req *fuse.OpenRequest, res *fuse.Open
if err != nil {
roFileOpenError.Incr()
Logger.Printf("roFile.Open: %v", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}

// Turn off the OpenDirectIO bit (on by default in rsc fuse server.go),
diff --git a/pkg/fs/root.go b/pkg/fs/root.go
index c27177f..cfc4d4f 100644
--- a/pkg/fs/root.go
+++ b/pkg/fs/root.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -136,8 +137,12 @@ func (n *root) Lookup(ctx context.Context, name string) (fs.Node, error) {
if br, ok := blob.Parse(name); ok {
Logger.Printf("Root lookup of blobref. %q => %v", name, br)
node := &node{fs: n.fs, blobref: br}
- if _, err := node.schema(); os.IsNotExist(err) {
- return nil, fuse.ENOENT
+ if _, err := node.schema(ctx); err != nil {
+ if os.IsNotExist(err) {
+ return nil, fuse.ENOENT
+ } else {
+ return nil, handleEIOorEINTR(err)
+ }
}
return node, nil
}
diff --git a/pkg/fs/roots.go b/pkg/fs/roots.go
index f2ba28b..e9b3545 100644
--- a/pkg/fs/roots.go
+++ b/pkg/fs/roots.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -76,7 +77,7 @@ func (n *rootsDir) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
n.mu.Lock()
defer n.mu.Unlock()
if err := n.condRefresh(ctx); err != nil {
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
var ents []fuse.Dirent
for name := range n.m {
@@ -94,7 +95,7 @@ func (n *rootsDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
defer n.mu.Unlock()

if err := n.condRefresh(ctx); err != nil {
- return err
+ return handleEIOorEINTR(err)
}
br := n.m[req.Name]
if !br.Valid() {
@@ -105,7 +106,7 @@ func (n *rootsDir) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
_, err := n.fs.client.UploadAndSignBlob(ctx, claim)
if err != nil {
Logger.Println("rootsDir.Remove:", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}

delete(n.m, req.Name)
@@ -139,7 +140,7 @@ func (n *rootsDir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir f
res, err := n.fs.client.Describe(ctx, &search.DescribeRequest{BlobRef: target})
if err != nil {
Logger.Println("rootsDir.Rename:", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}
db := res.Meta[target.String()]
if db == nil {
@@ -159,7 +160,7 @@ func (n *rootsDir) Rename(ctx context.Context, req *fuse.RenameRequest, newDir f
_, err = n.fs.client.UploadAndSignBlob(ctx, claim)
if err != nil {
Logger.Printf("Upload rename link error: %v", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}

// Comment transplanted from mutDir.Rename
@@ -185,7 +186,7 @@ func (n *rootsDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
n.mu.Lock()
defer n.mu.Unlock()
if err := n.condRefresh(ctx); err != nil {
- return nil, err
+ return nil, handleEIOorEINTR(err)
}
br := n.m[name]
if !br.Valid() {
@@ -204,7 +205,8 @@ func (n *rootsDir) Lookup(ctx context.Context, name string) (fs.Node, error) {
fs: n.fs,
permanode: br,
name: name,
- xattrs: map[string][]byte{},
+ xattrs: make(map[string][]byte),
+ children: make(map[string]mutFileOrDir),
}
}
n.children[name] = nod
@@ -232,7 +234,7 @@ func (n *rootsDir) condRefresh(ctx context.Context) error {
})
if err := grp.Err(); err != nil {
Logger.Printf("fs.roots: error refreshing permanodes: %v", err)
- return fuse.EIO
+ return err
}

n.m = make(map[string]blob.Ref)
@@ -256,7 +258,7 @@ func (n *rootsDir) condRefresh(ctx context.Context) error {
dres, err := n.fs.client.Describe(ctx, dr)
if err != nil {
Logger.Printf("Describe failure: %v", err)
- return fuse.EIO
+ return err
}

// Roots
@@ -310,7 +312,7 @@ func (n *rootsDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node,
pr, err := n.fs.client.UploadNewPermanode(ctx)
if err != nil {
Logger.Printf("rootsDir.Create(%q): %v", name, err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}

var grp syncutil.Group
@@ -328,14 +330,15 @@ func (n *rootsDir) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node,
})
if err := grp.Err(); err != nil {
Logger.Printf("rootsDir.Create(%q): %v", name, err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}

nod := &mutDir{
fs: n.fs,
permanode: pr.BlobRef,
name: name,
- xattrs: map[string][]byte{},
+ xattrs: make(map[string][]byte),
+ children: make(map[string]mutFileOrDir),
}
n.mu.Lock()
n.m[name] = pr.BlobRef
diff --git a/pkg/fs/rover.go b/pkg/fs/rover.go
index 854ff4c..9d58922 100644
--- a/pkg/fs/rover.go
+++ b/pkg/fs/rover.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -21,6 +22,7 @@ package fs
import (
"context"
"errors"
+ "fmt"
"os"
"path/filepath"
"strings"
@@ -96,7 +98,7 @@ func (n *roVersionsDir) populate(ctx context.Context) error {
})
if err != nil {
Logger.Println("roVersionsDir.paths:", err)
- return nil
+ return fmt.Errorf("error while describing permanode: %w", err)
}
db := res.Meta[n.permanode.String()]
if db == nil {
@@ -169,7 +171,7 @@ func (n *roVersionsDir) populate(ctx context.Context) error {
func (n *roVersionsDir) ReadDir(ctx context.Context) ([]fuse.Dirent, error) {
if err := n.populate(ctx); err != nil {
Logger.Println("populate:", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
n.mu.Lock()
defer n.mu.Unlock()
@@ -203,7 +205,7 @@ func (n *roVersionsDir) Lookup(ctx context.Context, name string) (ret fs.Node, e
}()
if err := n.populate(ctx); err != nil {
Logger.Println("populate:", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
n.mu.Lock()
defer n.mu.Unlock()
@@ -261,7 +263,7 @@ func (n *roFileVersionsDir) populate(ctx context.Context) error {
Logger.Printf("roFileVersionsDir.populate(%q)", n.fullPath())
res, err := n.fs.client.GetClaims(ctx, &search.ClaimsRequest{Permanode: n.permanode, AttrFilter: "camliContent"})
if err != nil {
- return errors.New("error while getting claims")
+ return fmt.Errorf("error while getting claims: %w", err)
}

n.children = make(map[string]roFileOrDir)
@@ -276,7 +278,7 @@ func (n *roFileVersionsDir) populate(ctx context.Context) error {
At: cl.Date,
})
if err != nil {
- return errors.New("blobref not described")
+ return fmt.Errorf("blobref not described: %w", err)
}
db := res.Meta[cl.Value]
if db == nil {
@@ -300,7 +302,7 @@ func (n *roFileVersionsDir) populate(ctx context.Context) error {
func (n *roFileVersionsDir) ReadDir(ctx context.Context) ([]fuse.Dirent, error) {
if err := n.populate(ctx); err != nil {
Logger.Println("populate:", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
n.mu.Lock()
defer n.mu.Unlock()
@@ -334,7 +336,7 @@ func (n *roFileVersionsDir) Lookup(ctx context.Context, name string) (ret fs.Nod
}()
if err := n.populate(ctx); err != nil {
Logger.Println("populate:", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
n.mu.Lock()
defer n.mu.Unlock()
@@ -373,7 +375,7 @@ func (n *roFileVersion) Open(ctx context.Context, req *fuse.OpenRequest, res *fu
if err != nil {
roFileOpenError.Incr()
Logger.Printf("roFile.Open: %v", err)
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}

// Turn off the OpenDirectIO bit (on by default in rsc fuse server.go),
diff --git a/pkg/fs/time.go b/pkg/fs/time.go
index 3377928..421d019 100644
--- a/pkg/fs/time.go
+++ b/pkg/fs/time.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
diff --git a/pkg/fs/time_test.go b/pkg/fs/time_test.go
index 974ca9a..3c00209 100644
--- a/pkg/fs/time_test.go
+++ b/pkg/fs/time_test.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
diff --git a/pkg/fs/util.go b/pkg/fs/util.go
index 9432e5b..8a9c3b1 100644
--- a/pkg/fs/util.go
+++ b/pkg/fs/util.go
@@ -1,3 +1,6 @@
+//go:build linux || darwin
+// +build linux darwin
+
/*
Copyright 2013 The Perkeep Authors

@@ -17,10 +20,13 @@ limitations under the License.
package fs

import (
+ "context"
"errors"
"os/exec"
"runtime"
"time"
+
+ "bazil.org/fuse"
)

// Unmount attempts to unmount the provided FUSE mount point, forcibly
@@ -51,3 +57,14 @@ func Unmount(point string) error {
return err
}
}
+
+func handleEIOorEINTR(err error) error {
+ if err == nil {
+ return nil
+ }
+ if errors.Is(err, context.Canceled) {
+ return fuse.EINTR
+ } else {
+ return fuse.EIO
+ }
+}
diff --git a/pkg/fs/versions.go b/pkg/fs/versions.go
index 625be32..001495d 100644
--- a/pkg/fs/versions.go
+++ b/pkg/fs/versions.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -72,7 +73,7 @@ func (n *versionsDir) ReadDir(ctx context.Context) ([]fuse.Dirent, error) {
n.mu.Lock()
defer n.mu.Unlock()
if err := n.condRefresh(ctx); err != nil {
- return nil, fuse.EIO
+ return nil, handleEIOorEINTR(err)
}
var ents []fuse.Dirent
for name := range n.m {
@@ -87,7 +88,7 @@ func (n *versionsDir) Lookup(ctx context.Context, name string) (fs.Node, error)
n.mu.Lock()
defer n.mu.Unlock()
if err := n.condRefresh(ctx); err != nil {
- return nil, err
+ return nil, handleEIOorEINTR(err)
}
br := n.m[name]
if !br.Valid() {
@@ -126,7 +127,7 @@ func (n *versionsDir) condRefresh(ctx context.Context) error {
})
if err := grp.Err(); err != nil {
Logger.Printf("fs.versions: GetRecentPermanodes error in ReadDir: %v", err)
- return fuse.EIO
+ return err
}

n.m = make(map[string]blob.Ref)
@@ -150,7 +151,7 @@ func (n *versionsDir) condRefresh(ctx context.Context) error {
dres, err := n.fs.client.Describe(ctx, dr)
if err != nil {
Logger.Printf("Describe failure: %v", err)
- return fuse.EIO
+ return err
}

// Roots
diff --git a/pkg/fs/xattr.go b/pkg/fs/xattr.go
index 2c90c87..ef4a42b 100644
--- a/pkg/fs/xattr.go
+++ b/pkg/fs/xattr.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
@@ -81,7 +82,7 @@ func (x *xattr) set(ctx context.Context, req *fuse.SetxattrRequest) error {
_, err := x.fs.client.UploadAndSignBlob(ctx, claim)
if err != nil {
Logger.Printf("Error setting xattr: %v", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}

val := make([]byte, len(req.Xattr))
@@ -101,7 +102,7 @@ func (x *xattr) remove(ctx context.Context, req *fuse.RemovexattrRequest) error

if err != nil {
Logger.Printf("Error removing xattr: %v", err)
- return fuse.EIO
+ return handleEIOorEINTR(err)
}

x.mu.Lock()
diff --git a/pkg/fs/z_test.go b/pkg/fs/z_test.go
index c9f9d36..a0d050a 100644
--- a/pkg/fs/z_test.go
+++ b/pkg/fs/z_test.go
@@ -1,3 +1,4 @@
+//go:build linux || darwin
// +build linux darwin

/*
diff --git a/pkg/schema/filereader.go b/pkg/schema/filereader.go
index f50aacf..128ef2b 100644
--- a/pkg/schema/filereader.go
+++ b/pkg/schema/filereader.go
@@ -76,12 +76,12 @@ func NewFileReader(ctx context.Context, fetcher blob.Fetcher, fileBlobRef blob.R
}
rc, _, err := fetcher.Fetch(ctx, fileBlobRef)
if err != nil {
- return nil, fmt.Errorf("schema/filereader: fetching file schema blob: %v", err)
+ return nil, fmt.Errorf("schema/filereader: fetching file schema blob: %w", err)
}
defer rc.Close()
ss, err := parseSuperset(rc)
if err != nil {
- return nil, fmt.Errorf("schema/filereader: decoding file schema blob: %v", err)
+ return nil, fmt.Errorf("schema/filereader: decoding file schema blob: %w", err)
}
ss.BlobRef = fileBlobRef
if ss.Type != "file" && ss.Type != "bytes" {
@@ -89,7 +89,7 @@ func NewFileReader(ctx context.Context, fetcher blob.Fetcher, fileBlobRef blob.R
}
fr, err := ss.NewFileReader(fetcher)
if err != nil {
- return nil, fmt.Errorf("schema/filereader: creating FileReader for %s: %v", fileBlobRef, err)
+ return nil, fmt.Errorf("schema/filereader: creating FileReader for %s: %w", fileBlobRef, err)
}
return fr, nil
}
@@ -290,7 +290,7 @@ func (fr *FileReader) getSuperset(ctx context.Context, br blob.Ref) (*superset,
}
rc, _, err := fr.fetcher.Fetch(ctx, br)
if err != nil {
- return nil, fmt.Errorf("schema/filereader: fetching file schema blob: %v", err)
+ return nil, fmt.Errorf("schema/filereader: fetching file schema blob: %w", err)
}
defer rc.Close()
ss, err = parseSuperset(rc)
diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go
index a4d7637..ac7ad67 100644
--- a/pkg/schema/schema.go
+++ b/pkg/schema/schema.go
@@ -231,7 +231,7 @@ func NewDirectoryEntryFromBlobRef(ctx context.Context, fetcher blob.Fetcher, blo
ss := new(superset)
err := ss.setFromBlobRef(ctx, fetcher, blobRef)
if err != nil {
- return nil, fmt.Errorf("schema/filereader: can't fill superset: %v", err)
+ return nil, fmt.Errorf("schema/filereader: can't fill superset: %w", err)
}
return newDirectoryEntry(fetcher, ss)
}
@@ -1011,7 +1011,7 @@ func FileTime(f io.ReaderAt) (time.Time, error) {
if osf, ok := f.(*os.File); ok {
fi, err := osf.Stat()
if err != nil {
- return ct, fmt.Errorf("Failed to find a modtime: stat: %v", err)
+ return ct, fmt.Errorf("Failed to find a modtime: stat: %w", err)
}
return fi.ModTime(), nil
}
Reply all
Reply to author
Forward
0 new messages