// Copyright 2020 The gVisor Authors. // // 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 import ( "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/sentry/kernel/auth" ) // consts used by FUSE_INIT negotiation. const ( // fuseMaxMaxPages is the maximum value for MaxPages received in InitOut. // Follow the same behavior as unix fuse implementation. fuseMaxMaxPages = 256 // Maximum value for the time granularity for file time stamps, 1s. // Follow the same behavior as unix fuse implementation. fuseMaxTimeGranNs = 1000000000 // Minimum value for MaxWrite and MaxRead. // Follow the same behavior as unix fuse implementation. fuseMinMaxWrite = 4096 fuseMinMaxRead = 4096 // Temporary default value for max readahead, 128kb. fuseDefaultMaxReadahead = 131072 // The FUSE_INIT_IN flags sent to the daemon. // TODO(gvisor.dev/issue/3199): complete the flags. fuseDefaultInitFlags = linux.FUSE_MAX_PAGES ) // Adjustable maximums for Connection's cogestion control parameters. // Used as the upperbound of the config values. // Currently we do not support adjustment to them. var ( MaxUserBackgroundRequest uint16 = fuseDefaultMaxBackground MaxUserCongestionThreshold uint16 = fuseDefaultCongestionThreshold ) // InitSend sends a FUSE_INIT request. func (conn *connection) InitSend(creds *auth.Credentials, pid uint32) error { in := linux.FUSEInitIn{ Major: linux.FUSE_KERNEL_VERSION, Minor: linux.FUSE_KERNEL_MINOR_VERSION, // TODO(gvisor.dev/issue/3196): find appropriate way to calculate this MaxReadahead: fuseDefaultMaxReadahead, Flags: fuseDefaultInitFlags, } req, err := conn.NewRequest(creds, pid, 0, linux.FUSE_INIT, &in) if err != nil { return err } // Since there is no task to block on and FUSE_INIT is the request // to unblock other requests, use nil. return conn.CallAsync(nil, req) } // InitRecv receives a FUSE_INIT reply and process it. func (conn *connection) InitRecv(res *Response, hasSysAdminCap bool) error { if err := res.Error(); err != nil { return err } var out linux.FUSEInitOut if err := res.UnmarshalPayload(&out); err != nil { return err } return conn.initProcessReply(&out, hasSysAdminCap) } // Process the FUSE_INIT reply from the FUSE server. func (conn *connection) initProcessReply(out *linux.FUSEInitOut, hasSysAdminCap bool) error { // No support for old major fuse versions. if out.Major != linux.FUSE_KERNEL_VERSION { conn.connInitError = true // Set the connection as initialized and unblock the blocked requests // (i.e. return error for them). conn.SetInitialized() return nil } // Start processing the reply. conn.connInitSuccess = true conn.minor = out.Minor // No support for limits before minor version 13. if out.Minor >= 13 { conn.bgLock.Lock() if out.MaxBackground > 0 { conn.maxBackground = out.MaxBackground if !hasSysAdminCap && conn.maxBackground > MaxUserBackgroundRequest { conn.maxBackground = MaxUserBackgroundRequest } } if out.CongestionThreshold > 0 { conn.congestionThreshold = out.CongestionThreshold if !hasSysAdminCap && conn.congestionThreshold > MaxUserCongestionThreshold { conn.congestionThreshold = MaxUserCongestionThreshold } } conn.bgLock.Unlock() } // No support for the following flags before minor version 6. if out.Minor >= 6 { conn.asyncRead = out.Flags&linux.FUSE_ASYNC_READ != 0 conn.bigWrites = out.Flags&linux.FUSE_BIG_WRITES != 0 conn.dontMask = out.Flags&linux.FUSE_DONT_MASK != 0 conn.writebackCache = out.Flags&linux.FUSE_WRITEBACK_CACHE != 0 conn.cacheSymlinks = out.Flags&linux.FUSE_CACHE_SYMLINKS != 0 conn.abortErr = out.Flags&linux.FUSE_ABORT_ERROR != 0 // TODO(gvisor.dev/issue/3195): figure out how to use TimeGran (0 < TimeGran <= fuseMaxTimeGranNs). if out.Flags&linux.FUSE_MAX_PAGES != 0 { maxPages := out.MaxPages if maxPages < 1 { maxPages = 1 } if maxPages > fuseMaxMaxPages { maxPages = fuseMaxMaxPages } conn.maxPages = maxPages } } // No support for negotiating MaxWrite before minor version 5. if out.Minor >= 5 { conn.maxWrite = out.MaxWrite } else { conn.maxWrite = fuseMinMaxWrite } if conn.maxWrite < fuseMinMaxWrite { conn.maxWrite = fuseMinMaxWrite } // Set connection as initialized and unblock the requests // issued before init. conn.SetInitialized() return nil }