Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type Connection struct {
cfg MountConfig
debugLogger *log.Logger
errorLogger *log.Logger
wireLogger io.Writer

// The device through which we're talking to the kernel, and the protocol
// version that we're using to talk to it.
Expand All @@ -87,6 +88,18 @@ type opState struct {
inMsg *buffer.InMessage
outMsg *buffer.OutMessage
op interface{}
wlog *WireLogRecord
}

// Return the current wirelog record from the context if the MountConfig
// contained a non-nil wireLogger, nil otherwise.
func GetWirelog(ctx context.Context) *WireLogRecord {
val := ctx.Value(contextKey)
state, ok := val.(opState)
if ok {
return state.wlog
}
return nil
}

// Create a connection wrapping the supplied file descriptor connected to the
Expand All @@ -97,11 +110,13 @@ func newConnection(
cfg MountConfig,
debugLogger *log.Logger,
errorLogger *log.Logger,
wireLogger io.Writer,
dev *os.File) (*Connection, error) {
c := &Connection{
cfg: cfg,
debugLogger: debugLogger,
errorLogger: errorLogger,
wireLogger: wireLogger,
dev: dev,
cancelFuncs: make(map[uint64]func()),
}
Expand Down Expand Up @@ -461,7 +476,11 @@ func (c *Connection) ReadOp() (_ context.Context, op interface{}, _ error) {

// Set up a context that remembers information about this op.
ctx := c.beginOp(inMsg.Header().Opcode, inMsg.Header().Unique)
ctx = context.WithValue(ctx, contextKey, opState{inMsg, outMsg, op})
var wlog *WireLogRecord
if c.wireLogger != nil {
wlog = NewWireLogRecord()
}
ctx = context.WithValue(ctx, contextKey, opState{inMsg, outMsg, op, wlog})

// Return the op to the user.
return ctx, op, nil
Expand Down Expand Up @@ -573,6 +592,13 @@ func (c *Connection) Reply(ctx context.Context, opErr error) error {
outMsg.Sglist = nil
}

if c.wireLogger != nil {
entry, err := formatWireLogEntry(op, opErr, state.wlog)
if err == nil {
c.wireLogger.Write(entry)
}
}

return nil
}

Expand Down
1 change: 1 addition & 0 deletions mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ func Mount(
cfgCopy,
config.DebugLogger,
config.ErrorLogger,
config.WireLogger,
dev)
if err != nil {
return nil, fmt.Errorf("newConnection: %v", err)
Expand Down
5 changes: 5 additions & 0 deletions mount_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ package fuse
import (
"context"
"fmt"
"io"
"log"
"runtime"
"strings"
Expand Down Expand Up @@ -47,6 +48,10 @@ type MountConfig struct {
// performed.
DebugLogger *log.Logger

// A logger to use for logging fuse wire requests. If nil, no wire logging is
// performed.
WireLogger io.Writer

// Linux only. OS X always behaves as if writeback caching is disabled.
//
// By default on Linux we allow the kernel to perform writeback caching
Expand Down
154 changes: 154 additions & 0 deletions samples/wirelog/testfs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// Copyright 2025 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package wirelog

import (
"context"
"io"
"os"
"strings"

"github.com/jacobsa/fuse"
"github.com/jacobsa/fuse/fuseops"
"github.com/jacobsa/fuse/fuseutil"
)

// NewTestFS returns a simple file system with a root directory and one file "foo".
func NewTestFS() fuse.Server {
return fuseutil.NewFileSystemServer(&testFS{})
}

type testFS struct {
fuseutil.NotImplementedFileSystem
}

const (
rootInode fuseops.InodeID = fuseops.RootInodeID + iota
fileInode
)

var fileName string = "foo"
var fileContents string = "bar"
var fileMode os.FileMode = 0444
var fileHandle fuseops.HandleID = 10

func (fs *testFS) LookUpInode(
ctx context.Context,
op *fuseops.LookUpInodeOp) error {
if wlog := fuse.GetWirelog(ctx); wlog != nil {
wlog.Extra["lookup"] = "yes"
}
if op.Parent == rootInode && op.Name == fileName {
op.Entry.Child = fileInode
op.Entry.Attributes = fuseops.InodeAttributes{
Nlink: 1,
Mode: fileMode,
Size: uint64(len(fileContents)),
}
return nil
}
return fuse.ENOENT
}

func (fs *testFS) GetInodeAttributes(
ctx context.Context,
op *fuseops.GetInodeAttributesOp) error {
switch op.Inode {
case rootInode:
op.Attributes = fuseops.InodeAttributes{
Nlink: 1,
Mode: 0555 | os.ModeDir,
}
case fileInode:
op.Attributes = fuseops.InodeAttributes{
Nlink: 1,
Mode: fileMode,
Size: uint64(len(fileContents)),
}
default:
return fuse.ENOENT
}
return nil
}

func (fs *testFS) OpenDir(
ctx context.Context,
op *fuseops.OpenDirOp) error {
if op.Inode == rootInode {
return nil
}
return fuse.ENOENT
}

func (fs *testFS) ReadDir(
ctx context.Context,
op *fuseops.ReadDirOp) error {
if op.Inode != rootInode {
return fuse.ENOENT
}

entries := []fuseutil.Dirent{
{
Offset: 1,
Inode: fileInode,
Name: fileName,
Type: fuseutil.DT_File,
},
}

if op.Offset > fuseops.DirOffset(len(entries)) {
return nil
}

for _, e := range entries[op.Offset:] {
n := fuseutil.WriteDirent(op.Dst[op.BytesRead:], e)
if n == 0 {
break
}
op.BytesRead += n
}
return nil
}

func (fs *testFS) OpenFile(
ctx context.Context,
op *fuseops.OpenFileOp) error {
if op.Inode == fileInode {
op.Handle = fileHandle
return nil
}
return fuse.ENOENT
}

func (fs *testFS) FlushFile(
ctx context.Context,
op *fuseops.FlushFileOp) error {
return nil
}

func (fs *testFS) ReadFile(
ctx context.Context,
op *fuseops.ReadFileOp) error {
if op.Inode != fileInode {
return fuse.ENOENT
}
reader := strings.NewReader(fileContents)
var err error
op.BytesRead, err = reader.ReadAt(op.Dst, op.Offset)
if err == io.EOF {
return nil
}
return err
}
Loading