2015-03-24 07:19:42 +03:00
|
|
|
// Copyright 2015 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 fuse
|
|
|
|
|
2015-03-24 07:23:19 +03:00
|
|
|
import (
|
2015-04-29 04:44:00 +03:00
|
|
|
"fmt"
|
2015-07-24 08:07:55 +03:00
|
|
|
"io"
|
2015-03-24 07:23:19 +03:00
|
|
|
"log"
|
2015-07-24 08:07:55 +03:00
|
|
|
"os"
|
2015-04-29 04:44:00 +03:00
|
|
|
"path"
|
|
|
|
"runtime"
|
2015-04-02 03:10:55 +03:00
|
|
|
"sync"
|
2015-07-24 08:07:55 +03:00
|
|
|
"syscall"
|
2015-03-24 07:23:19 +03:00
|
|
|
|
2015-05-01 04:17:15 +03:00
|
|
|
"golang.org/x/net/context"
|
|
|
|
|
2015-08-03 06:15:29 +03:00
|
|
|
"github.com/jacobsa/fuse/fuseops"
|
2015-07-24 07:33:27 +03:00
|
|
|
"github.com/jacobsa/fuse/internal/buffer"
|
2015-07-28 05:59:44 +03:00
|
|
|
"github.com/jacobsa/fuse/internal/freelist"
|
2015-07-24 03:19:21 +03:00
|
|
|
"github.com/jacobsa/fuse/internal/fusekernel"
|
2015-03-24 07:23:19 +03:00
|
|
|
)
|
2015-03-24 07:19:42 +03:00
|
|
|
|
2015-07-27 08:08:41 +03:00
|
|
|
type contextKeyType uint64
|
|
|
|
|
2015-07-28 05:52:38 +03:00
|
|
|
var contextKey interface{} = contextKeyType(0)
|
2015-07-27 08:08:41 +03:00
|
|
|
|
2015-07-24 09:03:24 +03:00
|
|
|
// Ask the Linux kernel for larger read requests.
|
|
|
|
//
|
|
|
|
// As of 2015-03-26, the behavior in the kernel is:
|
|
|
|
//
|
|
|
|
// * (http://goo.gl/bQ1f1i, http://goo.gl/HwBrR6) Set the local variable
|
|
|
|
// ra_pages to be init_response->max_readahead divided by the page size.
|
|
|
|
//
|
|
|
|
// * (http://goo.gl/gcIsSh, http://goo.gl/LKV2vA) Set
|
|
|
|
// backing_dev_info::ra_pages to the min of that value and what was sent
|
|
|
|
// in the request's max_readahead field.
|
|
|
|
//
|
|
|
|
// * (http://goo.gl/u2SqzH) Use backing_dev_info::ra_pages when deciding
|
|
|
|
// how much to read ahead.
|
|
|
|
//
|
|
|
|
// * (http://goo.gl/JnhbdL) Don't read ahead at all if that field is zero.
|
|
|
|
//
|
|
|
|
// Reading a page at a time is a drag. Ask for a larger size.
|
|
|
|
const maxReadahead = 1 << 20
|
|
|
|
|
2015-03-24 07:19:42 +03:00
|
|
|
// A connection to the fuse kernel process.
|
|
|
|
type Connection struct {
|
2015-08-12 05:32:39 +03:00
|
|
|
cfg MountConfig
|
2015-05-25 07:13:24 +03:00
|
|
|
debugLogger *log.Logger
|
2015-05-25 07:17:16 +03:00
|
|
|
errorLogger *log.Logger
|
2015-04-29 04:53:17 +03:00
|
|
|
|
2015-07-24 08:32:50 +03:00
|
|
|
// The device through which we're talking to the kernel, and the protocol
|
|
|
|
// version that we're using to talk to it.
|
|
|
|
dev *os.File
|
|
|
|
protocol fusekernel.Protocol
|
|
|
|
|
2015-05-05 03:41:09 +03:00
|
|
|
mu sync.Mutex
|
|
|
|
|
2015-07-24 03:19:21 +03:00
|
|
|
// A map from fuse "unique" request ID (*not* the op ID for logging used
|
|
|
|
// above) to a function that cancel's its associated context.
|
2015-05-05 03:41:09 +03:00
|
|
|
//
|
|
|
|
// GUARDED_BY(mu)
|
2015-07-24 03:19:21 +03:00
|
|
|
cancelFuncs map[uint64]func()
|
2015-07-28 05:59:44 +03:00
|
|
|
|
|
|
|
// Freelists, serviced by freelists.go.
|
2015-07-28 09:13:18 +03:00
|
|
|
inMessages freelist.Freelist // GUARDED_BY(mu)
|
|
|
|
outMessages freelist.Freelist // GUARDED_BY(mu)
|
2015-03-24 07:19:42 +03:00
|
|
|
}
|
|
|
|
|
2015-07-27 07:52:35 +03:00
|
|
|
// State that is maintained for each in-flight op. This is stuffed into the
|
|
|
|
// context that the user uses to reply to the op.
|
|
|
|
type opState struct {
|
2015-07-29 03:51:24 +03:00
|
|
|
inMsg *buffer.InMessage
|
|
|
|
outMsg *buffer.OutMessage
|
|
|
|
op interface{}
|
2015-07-27 07:52:35 +03:00
|
|
|
}
|
|
|
|
|
2015-07-24 09:01:45 +03:00
|
|
|
// Create a connection wrapping the supplied file descriptor connected to the
|
|
|
|
// kernel. You must eventually call c.close().
|
2015-07-22 14:45:49 +03:00
|
|
|
//
|
|
|
|
// The loggers may be nil.
|
2015-03-24 07:34:50 +03:00
|
|
|
func newConnection(
|
2015-08-12 05:32:39 +03:00
|
|
|
cfg MountConfig,
|
2015-05-25 07:13:24 +03:00
|
|
|
debugLogger *log.Logger,
|
2015-05-25 07:17:16 +03:00
|
|
|
errorLogger *log.Logger,
|
2015-07-24 09:01:45 +03:00
|
|
|
dev *os.File) (c *Connection, err error) {
|
2015-07-24 09:29:59 +03:00
|
|
|
c = &Connection{
|
2015-08-12 05:32:39 +03:00
|
|
|
cfg: cfg,
|
2015-07-24 09:29:59 +03:00
|
|
|
debugLogger: debugLogger,
|
|
|
|
errorLogger: errorLogger,
|
|
|
|
dev: dev,
|
|
|
|
cancelFuncs: make(map[uint64]func()),
|
2015-07-24 09:01:45 +03:00
|
|
|
}
|
|
|
|
|
2015-07-24 09:29:59 +03:00
|
|
|
// Initialize.
|
|
|
|
err = c.Init()
|
2015-07-24 09:01:45 +03:00
|
|
|
if err != nil {
|
2015-07-24 09:29:59 +03:00
|
|
|
c.close()
|
|
|
|
err = fmt.Errorf("Init: %v", err)
|
2015-07-24 09:01:45 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-07-24 09:29:59 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do the work necessary to cause the mount process to complete.
|
|
|
|
func (c *Connection) Init() (err error) {
|
|
|
|
// Read the init op.
|
2015-07-27 08:10:00 +03:00
|
|
|
ctx, op, err := c.ReadOp()
|
2015-07-24 09:29:59 +03:00
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("Reading init op: %v", err)
|
|
|
|
return
|
2015-03-24 07:36:09 +03:00
|
|
|
}
|
|
|
|
|
2015-07-27 08:52:13 +03:00
|
|
|
initOp, ok := op.(*initOp)
|
2015-07-24 09:29:59 +03:00
|
|
|
if !ok {
|
2015-07-27 08:10:00 +03:00
|
|
|
c.Reply(ctx, syscall.EPROTO)
|
2015-07-27 08:52:13 +03:00
|
|
|
err = fmt.Errorf("Expected *initOp, got %T", op)
|
2015-07-24 09:29:59 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the protocol version spoken by the kernel is new enough.
|
|
|
|
min := fusekernel.Protocol{
|
|
|
|
fusekernel.ProtoVersionMinMajor,
|
|
|
|
fusekernel.ProtoVersionMinMinor,
|
|
|
|
}
|
|
|
|
|
|
|
|
if initOp.Kernel.LT(min) {
|
2015-07-27 08:10:00 +03:00
|
|
|
c.Reply(ctx, syscall.EPROTO)
|
2015-07-24 09:29:59 +03:00
|
|
|
err = fmt.Errorf("Version too old: %v", initOp.Kernel)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Downgrade our protocol if necessary.
|
|
|
|
c.protocol = fusekernel.Protocol{
|
|
|
|
fusekernel.ProtoVersionMaxMajor,
|
|
|
|
fusekernel.ProtoVersionMaxMinor,
|
|
|
|
}
|
|
|
|
|
|
|
|
if initOp.Kernel.LT(c.protocol) {
|
2015-07-24 09:35:14 +03:00
|
|
|
c.protocol = initOp.Kernel
|
2015-07-24 09:29:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Respond to the init op.
|
|
|
|
initOp.Library = c.protocol
|
|
|
|
initOp.MaxReadahead = maxReadahead
|
|
|
|
initOp.MaxWrite = buffer.MaxWriteSize
|
2015-07-29 07:05:22 +03:00
|
|
|
|
2015-07-29 07:20:34 +03:00
|
|
|
initOp.Flags = 0
|
|
|
|
|
2015-07-29 07:05:22 +03:00
|
|
|
// Tell the kernel not to use pitifully small 4 KiB writes.
|
|
|
|
initOp.Flags |= fusekernel.InitBigWrites
|
|
|
|
|
2015-08-12 05:32:39 +03:00
|
|
|
// Enable writeback caching if the user hasn't asked us not to.
|
|
|
|
if !c.cfg.DisableWritebackCaching {
|
|
|
|
initOp.Flags |= fusekernel.InitWritebackCache
|
|
|
|
}
|
2015-08-11 08:54:41 +03:00
|
|
|
|
2015-07-27 08:10:00 +03:00
|
|
|
c.Reply(ctx, nil)
|
2015-03-24 07:36:09 +03:00
|
|
|
return
|
|
|
|
}
|
2015-03-24 07:23:19 +03:00
|
|
|
|
2015-04-29 04:53:17 +03:00
|
|
|
// Log information for an operation with the given ID. calldepth is the depth
|
|
|
|
// to use when recovering file:line information with runtime.Caller.
|
2015-05-25 07:13:24 +03:00
|
|
|
func (c *Connection) debugLog(
|
2015-07-29 07:29:31 +03:00
|
|
|
fuseID uint64,
|
2015-04-29 04:44:00 +03:00
|
|
|
calldepth int,
|
2015-04-29 04:32:21 +03:00
|
|
|
format string,
|
|
|
|
v ...interface{}) {
|
2015-07-22 14:45:49 +03:00
|
|
|
if c.debugLogger == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-04-29 04:44:00 +03:00
|
|
|
// Get file:line info.
|
|
|
|
var file string
|
|
|
|
var line int
|
|
|
|
var ok bool
|
|
|
|
|
|
|
|
_, file, line, ok = runtime.Caller(calldepth)
|
|
|
|
if !ok {
|
|
|
|
file = "???"
|
|
|
|
}
|
|
|
|
|
2015-04-29 05:11:34 +03:00
|
|
|
fileLine := fmt.Sprintf("%v:%v", path.Base(file), line)
|
|
|
|
|
2015-04-29 04:44:00 +03:00
|
|
|
// Format the actual message to be printed.
|
|
|
|
msg := fmt.Sprintf(
|
2015-04-29 05:11:34 +03:00
|
|
|
"Op 0x%08x %24s] %v",
|
2015-07-29 07:29:31 +03:00
|
|
|
fuseID,
|
2015-04-29 05:11:34 +03:00
|
|
|
fileLine,
|
2015-04-29 04:44:00 +03:00
|
|
|
fmt.Sprintf(format, v...))
|
|
|
|
|
|
|
|
// Print it.
|
2015-05-25 07:13:24 +03:00
|
|
|
c.debugLogger.Println(msg)
|
2015-04-29 04:32:21 +03:00
|
|
|
}
|
|
|
|
|
2015-05-05 03:41:09 +03:00
|
|
|
// LOCKS_EXCLUDED(c.mu)
|
|
|
|
func (c *Connection) recordCancelFunc(
|
2015-07-24 03:19:21 +03:00
|
|
|
fuseID uint64,
|
2015-05-05 03:41:52 +03:00
|
|
|
f func()) {
|
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
2015-07-24 03:19:21 +03:00
|
|
|
if _, ok := c.cancelFuncs[fuseID]; ok {
|
|
|
|
panic(fmt.Sprintf("Already have cancel func for request %v", fuseID))
|
2015-05-05 03:41:52 +03:00
|
|
|
}
|
|
|
|
|
2015-07-24 03:19:21 +03:00
|
|
|
c.cancelFuncs[fuseID] = f
|
2015-05-05 03:41:52 +03:00
|
|
|
}
|
2015-05-05 03:41:09 +03:00
|
|
|
|
2015-05-05 03:14:54 +03:00
|
|
|
// Set up state for an op that is about to be returned to the user, given its
|
2015-07-24 03:19:21 +03:00
|
|
|
// underlying fuse opcode and request ID.
|
2015-05-05 03:14:54 +03:00
|
|
|
//
|
|
|
|
// Return a context that should be used for the op.
|
2015-05-05 03:41:09 +03:00
|
|
|
//
|
|
|
|
// LOCKS_EXCLUDED(c.mu)
|
2015-05-05 04:44:54 +03:00
|
|
|
func (c *Connection) beginOp(
|
2015-07-24 03:19:21 +03:00
|
|
|
opCode uint32,
|
|
|
|
fuseID uint64) (ctx context.Context) {
|
2015-07-16 03:54:34 +03:00
|
|
|
// Start with the parent context.
|
2015-08-12 05:32:39 +03:00
|
|
|
ctx = c.cfg.OpContext
|
2015-05-05 08:06:01 +03:00
|
|
|
|
2015-05-05 03:41:09 +03:00
|
|
|
// Set up a cancellation function.
|
2015-05-05 04:44:54 +03:00
|
|
|
//
|
2015-06-03 01:51:28 +03:00
|
|
|
// Special case: On Darwin, osxfuse aggressively reuses "unique" request IDs.
|
|
|
|
// This matters for Forget requests, which have no reply associated and
|
|
|
|
// therefore have IDs that are immediately eligible for reuse. For these, we
|
|
|
|
// should not record any state keyed on their ID.
|
2015-05-05 04:44:54 +03:00
|
|
|
//
|
|
|
|
// Cf. https://github.com/osxfuse/osxfuse/issues/208
|
2015-07-24 03:19:21 +03:00
|
|
|
if opCode != fusekernel.OpForget {
|
2015-05-05 04:44:54 +03:00
|
|
|
var cancel func()
|
|
|
|
ctx, cancel = context.WithCancel(ctx)
|
2015-07-24 03:19:21 +03:00
|
|
|
c.recordCancelFunc(fuseID, cancel)
|
2015-05-05 04:44:54 +03:00
|
|
|
}
|
2015-05-05 03:14:54 +03:00
|
|
|
|
|
|
|
return
|
2015-05-05 03:04:31 +03:00
|
|
|
}
|
2015-05-05 03:04:03 +03:00
|
|
|
|
2015-05-05 04:44:54 +03:00
|
|
|
// Clean up all state associated with an op to which the user has responded,
|
2015-07-24 03:19:21 +03:00
|
|
|
// given its underlying fuse opcode and request ID. This must be called before
|
|
|
|
// a response is sent to the kernel, to avoid a race where the request's ID
|
|
|
|
// might be reused by osxfuse.
|
2015-05-05 03:41:09 +03:00
|
|
|
//
|
|
|
|
// LOCKS_EXCLUDED(c.mu)
|
2015-07-24 03:19:21 +03:00
|
|
|
func (c *Connection) finishOp(
|
|
|
|
opCode uint32,
|
|
|
|
fuseID uint64) {
|
2015-05-05 03:41:09 +03:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
// Even though the op is finished, context.WithCancel requires us to arrange
|
|
|
|
// for the cancellation function to be invoked. We also must remove it from
|
|
|
|
// our map.
|
2015-05-05 04:44:54 +03:00
|
|
|
//
|
|
|
|
// Special case: we don't do this for Forget requests. See the note in
|
|
|
|
// beginOp above.
|
2015-07-24 03:19:21 +03:00
|
|
|
if opCode != fusekernel.OpForget {
|
|
|
|
cancel, ok := c.cancelFuncs[fuseID]
|
2015-05-05 04:44:54 +03:00
|
|
|
if !ok {
|
2015-07-24 03:19:21 +03:00
|
|
|
panic(fmt.Sprintf("Unknown request ID in finishOp: %v", fuseID))
|
2015-05-05 04:44:54 +03:00
|
|
|
}
|
2015-05-05 03:41:09 +03:00
|
|
|
|
2015-05-05 04:44:54 +03:00
|
|
|
cancel()
|
2015-07-24 03:19:21 +03:00
|
|
|
delete(c.cancelFuncs, fuseID)
|
2015-05-05 04:44:54 +03:00
|
|
|
}
|
2015-05-05 03:04:31 +03:00
|
|
|
}
|
2015-05-05 03:04:03 +03:00
|
|
|
|
2015-05-05 05:21:57 +03:00
|
|
|
// LOCKS_EXCLUDED(c.mu)
|
2015-07-24 03:19:21 +03:00
|
|
|
func (c *Connection) handleInterrupt(fuseID uint64) {
|
2015-05-05 05:21:57 +03:00
|
|
|
c.mu.Lock()
|
|
|
|
defer c.mu.Unlock()
|
|
|
|
|
|
|
|
// NOTE(jacobsa): fuse.txt in the Linux kernel documentation
|
|
|
|
// (https://goo.gl/H55Dnr) defines the kernel <-> userspace protocol for
|
|
|
|
// interrupts.
|
|
|
|
//
|
|
|
|
// In particular, my reading of it is that an interrupt request cannot be
|
|
|
|
// delivered to userspace before the original request. The part about the
|
|
|
|
// race and EAGAIN appears to be aimed at userspace programs that
|
2015-06-03 01:51:28 +03:00
|
|
|
// concurrently process requests (cf. http://goo.gl/BES2rs).
|
2015-05-05 05:21:57 +03:00
|
|
|
//
|
2015-06-03 01:51:28 +03:00
|
|
|
// So in this method if we can't find the ID to be interrupted, it means that
|
|
|
|
// the request has already been replied to.
|
|
|
|
//
|
|
|
|
// Cf. https://github.com/osxfuse/osxfuse/issues/208
|
|
|
|
// Cf. http://comments.gmane.org/gmane.comp.file-systems.fuse.devel/14675
|
2015-07-24 03:19:21 +03:00
|
|
|
cancel, ok := c.cancelFuncs[fuseID]
|
2015-05-05 05:21:57 +03:00
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
cancel()
|
|
|
|
}
|
|
|
|
|
2015-07-24 07:33:27 +03:00
|
|
|
// Read the next message from the kernel. The message must later be destroyed
|
|
|
|
// using destroyInMessage.
|
|
|
|
func (c *Connection) readMessage() (m *buffer.InMessage, err error) {
|
2015-07-24 08:04:28 +03:00
|
|
|
// Allocate a message.
|
2015-07-28 05:59:44 +03:00
|
|
|
m = c.getInMessage()
|
2015-07-24 08:04:28 +03:00
|
|
|
|
|
|
|
// Loop past transient errors.
|
|
|
|
for {
|
2015-07-24 08:26:07 +03:00
|
|
|
// Attempt a reaed.
|
2015-07-24 08:32:50 +03:00
|
|
|
err = m.Init(c.dev)
|
2015-07-24 08:04:28 +03:00
|
|
|
|
2015-07-24 08:20:43 +03:00
|
|
|
// Special cases:
|
|
|
|
//
|
|
|
|
// * ENODEV means fuse has hung up.
|
|
|
|
//
|
|
|
|
// * EINTR means we should try again. (This seems to happen often on
|
|
|
|
// OS X, cf. http://golang.org/issue/11180)
|
|
|
|
//
|
|
|
|
if pe, ok := err.(*os.PathError); ok {
|
|
|
|
switch pe.Err {
|
|
|
|
case syscall.ENODEV:
|
2015-07-24 08:07:55 +03:00
|
|
|
err = io.EOF
|
2015-07-24 08:20:43 +03:00
|
|
|
|
|
|
|
case syscall.EINTR:
|
|
|
|
err = nil
|
|
|
|
continue
|
2015-07-24 08:07:55 +03:00
|
|
|
}
|
2015-07-24 08:20:43 +03:00
|
|
|
}
|
2015-07-24 08:07:55 +03:00
|
|
|
|
2015-07-24 08:20:43 +03:00
|
|
|
if err != nil {
|
2015-07-28 05:59:44 +03:00
|
|
|
c.putInMessage(m)
|
2015-07-24 08:20:43 +03:00
|
|
|
m = nil
|
2015-07-24 08:04:28 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
2015-07-24 07:33:27 +03:00
|
|
|
}
|
|
|
|
|
2015-07-24 08:31:16 +03:00
|
|
|
// Write the supplied message to the kernel.
|
|
|
|
func (c *Connection) writeMessage(msg []byte) (err error) {
|
|
|
|
// Avoid the retry loop in os.File.Write.
|
2015-07-24 08:32:50 +03:00
|
|
|
n, err := syscall.Write(int(c.dev.Fd()), msg)
|
2015-07-24 08:31:16 +03:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if n != len(msg) {
|
|
|
|
err = fmt.Errorf("Wrote %d bytes; expected %d", n, len(msg))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-07-27 07:52:35 +03:00
|
|
|
// Read the next op from the kernel process, returning the op and a context
|
|
|
|
// that should be used for work related to the op. Return io.EOF if the kernel
|
|
|
|
// has closed the connection.
|
|
|
|
//
|
|
|
|
// If err != nil, the user is responsible for later calling c.Reply with the
|
|
|
|
// returned context.
|
2015-03-24 07:23:19 +03:00
|
|
|
//
|
|
|
|
// This function delivers ops in exactly the order they are received from
|
2015-04-29 04:28:16 +03:00
|
|
|
// /dev/fuse. It must not be called multiple times concurrently.
|
2015-05-05 03:41:09 +03:00
|
|
|
//
|
|
|
|
// LOCKS_EXCLUDED(c.mu)
|
2015-07-27 08:43:41 +03:00
|
|
|
func (c *Connection) ReadOp() (ctx context.Context, op interface{}, err error) {
|
2015-03-24 07:26:02 +03:00
|
|
|
// Keep going until we find a request we know how to convert.
|
|
|
|
for {
|
2015-07-24 07:33:27 +03:00
|
|
|
// Read the next message from the kernel.
|
2015-07-29 03:51:24 +03:00
|
|
|
var inMsg *buffer.InMessage
|
|
|
|
inMsg, err = c.readMessage()
|
2015-03-24 07:26:02 +03:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-07-27 08:08:41 +03:00
|
|
|
// Convert the message to an op.
|
2015-07-29 04:02:29 +03:00
|
|
|
outMsg := c.getOutMessage()
|
|
|
|
op, err = convertInMessage(inMsg, outMsg, c.protocol)
|
2015-07-27 08:08:41 +03:00
|
|
|
if err != nil {
|
2015-07-29 04:02:29 +03:00
|
|
|
c.putOutMessage(outMsg)
|
2015-07-27 08:08:41 +03:00
|
|
|
err = fmt.Errorf("convertInMessage: %v", err)
|
2015-07-24 03:19:21 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2015-07-27 08:08:41 +03:00
|
|
|
// Choose an ID for this operation for the purposes of logging, and log it.
|
2015-07-29 08:13:22 +03:00
|
|
|
if c.debugLogger != nil {
|
|
|
|
c.debugLog(inMsg.Header().Unique, 1, "<- %s", describeRequest(op))
|
|
|
|
}
|
2015-07-24 03:19:21 +03:00
|
|
|
|
2015-07-27 08:08:41 +03:00
|
|
|
// Special case: handle interrupt requests inline.
|
2015-07-27 08:52:13 +03:00
|
|
|
if interruptOp, ok := op.(*interruptOp); ok {
|
2015-07-27 08:08:41 +03:00
|
|
|
c.handleInterrupt(interruptOp.FuseID)
|
|
|
|
continue
|
2015-07-24 03:19:21 +03:00
|
|
|
}
|
|
|
|
|
2015-07-27 08:08:41 +03:00
|
|
|
// Set up a context that remembers information about this op.
|
2015-07-29 03:51:24 +03:00
|
|
|
ctx = c.beginOp(inMsg.Header().Opcode, inMsg.Header().Unique)
|
2015-07-29 07:29:31 +03:00
|
|
|
ctx = context.WithValue(ctx, contextKey, opState{inMsg, outMsg, op})
|
2015-03-24 07:52:14 +03:00
|
|
|
|
2015-05-05 05:21:57 +03:00
|
|
|
// Special case: responding to statfs is required to make mounting work on
|
|
|
|
// OS X. We don't currently expose the capability for the file system to
|
2015-03-24 07:51:36 +03:00
|
|
|
// intercept this.
|
2015-07-27 08:52:13 +03:00
|
|
|
if _, ok := op.(*statFSOp); ok {
|
2015-07-27 08:08:41 +03:00
|
|
|
c.Reply(ctx, nil)
|
2015-05-05 05:21:57 +03:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2015-07-27 08:08:41 +03:00
|
|
|
// Return the op to the user.
|
2015-03-24 07:26:02 +03:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2015-03-24 07:34:50 +03:00
|
|
|
|
2015-08-03 06:15:29 +03:00
|
|
|
// Skip errors that happen as a matter of course, since they spook users.
|
|
|
|
func (c *Connection) shouldLogError(
|
|
|
|
op interface{},
|
|
|
|
err error) bool {
|
|
|
|
// We don't log non-errors.
|
|
|
|
if err == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can't log if there's nothing to log to.
|
|
|
|
if c.errorLogger == nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
switch op.(type) {
|
|
|
|
case *fuseops.LookUpInodeOp:
|
|
|
|
// It is totally normal for the kernel to ask to look up an inode by name
|
|
|
|
// and find the name doesn't exist. For example, this happens when linking
|
|
|
|
// a new file.
|
|
|
|
if err == syscall.ENOENT {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
case *unknownOp:
|
|
|
|
// Don't bother the user with methods we intentionally don't support.
|
|
|
|
if err == syscall.ENOSYS {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2015-07-27 08:12:43 +03:00
|
|
|
// Reply to an op previously read using ReadOp, with the supplied error (or nil
|
|
|
|
// if successful). The context must be the context returned by ReadOp.
|
|
|
|
//
|
|
|
|
// LOCKS_EXCLUDED(c.mu)
|
2015-07-27 08:21:07 +03:00
|
|
|
func (c *Connection) Reply(ctx context.Context, opErr error) {
|
|
|
|
// Extract the state we stuffed in earlier.
|
2015-07-28 05:52:38 +03:00
|
|
|
var key interface{} = contextKey
|
|
|
|
foo := ctx.Value(key)
|
|
|
|
state, ok := foo.(opState)
|
2015-07-27 08:21:07 +03:00
|
|
|
if !ok {
|
|
|
|
panic(fmt.Sprintf("Reply called with invalid context: %#v", ctx))
|
|
|
|
}
|
|
|
|
|
|
|
|
op := state.op
|
2015-07-29 03:51:24 +03:00
|
|
|
inMsg := state.inMsg
|
|
|
|
outMsg := state.outMsg
|
2015-07-29 07:29:31 +03:00
|
|
|
fuseID := inMsg.Header().Unique
|
2015-07-27 08:21:07 +03:00
|
|
|
|
2015-07-29 03:51:24 +03:00
|
|
|
// Make sure we destroy the messages when we're done.
|
|
|
|
defer c.putInMessage(inMsg)
|
|
|
|
defer c.putOutMessage(outMsg)
|
2015-07-27 08:21:07 +03:00
|
|
|
|
|
|
|
// Clean up state for this op.
|
2015-07-29 03:51:24 +03:00
|
|
|
c.finishOp(inMsg.Header().Opcode, inMsg.Header().Unique)
|
2015-07-27 08:21:07 +03:00
|
|
|
|
|
|
|
// Debug logging
|
|
|
|
if c.debugLogger != nil {
|
|
|
|
if opErr == nil {
|
2015-08-06 08:37:32 +03:00
|
|
|
c.debugLog(fuseID, 1, "-> OK")
|
2015-07-27 08:21:07 +03:00
|
|
|
} else {
|
2015-08-06 08:37:32 +03:00
|
|
|
c.debugLog(fuseID, 1, "-> Error: %q", opErr.Error())
|
2015-07-27 08:21:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Error logging
|
2015-08-03 06:15:29 +03:00
|
|
|
if c.shouldLogError(op, opErr) {
|
2015-07-27 09:03:58 +03:00
|
|
|
c.errorLogger.Printf("%T error: %v", op, opErr)
|
2015-07-27 08:21:07 +03:00
|
|
|
}
|
|
|
|
|
2015-07-28 09:14:58 +03:00
|
|
|
// Send the reply to the kernel, if one is required.
|
2015-07-29 03:51:24 +03:00
|
|
|
noResponse := c.kernelResponse(outMsg, inMsg.Header().Unique, op, opErr)
|
2015-07-28 09:14:58 +03:00
|
|
|
|
2015-07-29 03:39:37 +03:00
|
|
|
if !noResponse {
|
|
|
|
err := c.writeMessage(outMsg.Bytes())
|
2015-07-28 09:14:58 +03:00
|
|
|
if err != nil && c.errorLogger != nil {
|
|
|
|
c.errorLogger.Printf("writeMessage: %v", err)
|
2015-07-27 08:40:11 +03:00
|
|
|
}
|
2015-07-27 08:21:07 +03:00
|
|
|
}
|
2015-07-27 08:12:43 +03:00
|
|
|
}
|
|
|
|
|
2015-06-09 04:02:08 +03:00
|
|
|
// Close the connection. Must not be called until operations that were read
|
|
|
|
// from the connection have been responded to.
|
2015-03-24 07:36:09 +03:00
|
|
|
func (c *Connection) close() (err error) {
|
2015-07-24 08:35:31 +03:00
|
|
|
// Posix doesn't say that close can be called concurrently with read or
|
|
|
|
// write, but luckily we exclude the possibility of a race by requiring the
|
|
|
|
// user to respond to all ops first.
|
|
|
|
err = c.dev.Close()
|
2015-03-24 07:36:09 +03:00
|
|
|
return
|
|
|
|
}
|