github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fsimpl/fuse/connection_control.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fuse 16 17 import ( 18 "sync/atomic" 19 20 "golang.org/x/sys/unix" 21 "github.com/SagerNet/gvisor/pkg/abi/linux" 22 "github.com/SagerNet/gvisor/pkg/context" 23 "github.com/SagerNet/gvisor/pkg/sentry/kernel/auth" 24 ) 25 26 // consts used by FUSE_INIT negotiation. 27 const ( 28 // fuseMaxMaxPages is the maximum value for MaxPages received in InitOut. 29 // Follow the same behavior as unix fuse implementation. 30 fuseMaxMaxPages = 256 31 32 // Maximum value for the time granularity for file time stamps, 1s. 33 // Follow the same behavior as unix fuse implementation. 34 fuseMaxTimeGranNs = 1000000000 35 36 // Minimum value for MaxWrite and MaxRead. 37 // Follow the same behavior as unix fuse implementation. 38 fuseMinMaxWrite = 4096 39 fuseMinMaxRead = 4096 40 41 // Temporary default value for max readahead, 128kb. 42 fuseDefaultMaxReadahead = 131072 43 44 // The FUSE_INIT_IN flags sent to the daemon. 45 // TODO(github.com/SagerNet/issue/3199): complete the flags. 46 fuseDefaultInitFlags = linux.FUSE_MAX_PAGES 47 ) 48 49 // Adjustable maximums for Connection's cogestion control parameters. 50 // Used as the upperbound of the config values. 51 // Currently we do not support adjustment to them. 52 var ( 53 MaxUserBackgroundRequest uint16 = fuseDefaultMaxBackground 54 MaxUserCongestionThreshold uint16 = fuseDefaultCongestionThreshold 55 ) 56 57 // SetInitialized atomically sets the connection as initialized. 58 func (conn *connection) SetInitialized() { 59 // Unblock the requests sent before INIT. 60 close(conn.initializedChan) 61 62 // Close the channel first to avoid the non-atomic situation 63 // where conn.initialized is true but there are 64 // tasks being blocked on the channel. 65 // And it prevents the newer tasks from gaining 66 // unnecessary higher chance to be issued before the blocked one. 67 68 atomic.StoreInt32(&(conn.initialized), int32(1)) 69 } 70 71 // IsInitialized atomically check if the connection is initialized. 72 // pairs with SetInitialized(). 73 func (conn *connection) Initialized() bool { 74 return atomic.LoadInt32(&(conn.initialized)) != 0 75 } 76 77 // InitSend sends a FUSE_INIT request. 78 func (conn *connection) InitSend(creds *auth.Credentials, pid uint32) error { 79 in := linux.FUSEInitIn{ 80 Major: linux.FUSE_KERNEL_VERSION, 81 Minor: linux.FUSE_KERNEL_MINOR_VERSION, 82 // TODO(github.com/SagerNet/issue/3196): find appropriate way to calculate this 83 MaxReadahead: fuseDefaultMaxReadahead, 84 Flags: fuseDefaultInitFlags, 85 } 86 87 req := conn.NewRequest(creds, pid, 0, linux.FUSE_INIT, &in) 88 // Since there is no task to block on and FUSE_INIT is the request 89 // to unblock other requests, use nil. 90 return conn.CallAsync(nil, req) 91 } 92 93 // InitRecv receives a FUSE_INIT reply and process it. 94 // 95 // Preconditions: conn.asyncMu must not be held if minor verion is newer than 13. 96 func (conn *connection) InitRecv(res *Response, hasSysAdminCap bool) error { 97 if err := res.Error(); err != nil { 98 return err 99 } 100 101 initRes := fuseInitRes{initLen: res.DataLen()} 102 if err := res.UnmarshalPayload(&initRes); err != nil { 103 return err 104 } 105 106 return conn.initProcessReply(&initRes.initOut, hasSysAdminCap) 107 } 108 109 // Process the FUSE_INIT reply from the FUSE server. 110 // It tries to acquire the conn.asyncMu lock if minor version is newer than 13. 111 func (conn *connection) initProcessReply(out *linux.FUSEInitOut, hasSysAdminCap bool) error { 112 // No matter error or not, always set initialzied. 113 // to unblock the blocked requests. 114 defer conn.SetInitialized() 115 116 // No support for old major fuse versions. 117 if out.Major != linux.FUSE_KERNEL_VERSION { 118 conn.connInitError = true 119 return nil 120 } 121 122 // Start processing the reply. 123 conn.connInitSuccess = true 124 conn.minor = out.Minor 125 126 // No support for negotiating MaxWrite before minor version 5. 127 if out.Minor >= 5 { 128 conn.maxWrite = out.MaxWrite 129 } else { 130 conn.maxWrite = fuseMinMaxWrite 131 } 132 if conn.maxWrite < fuseMinMaxWrite { 133 conn.maxWrite = fuseMinMaxWrite 134 } 135 136 // No support for the following flags before minor version 6. 137 if out.Minor >= 6 { 138 conn.asyncRead = out.Flags&linux.FUSE_ASYNC_READ != 0 139 conn.bigWrites = out.Flags&linux.FUSE_BIG_WRITES != 0 140 conn.dontMask = out.Flags&linux.FUSE_DONT_MASK != 0 141 conn.writebackCache = out.Flags&linux.FUSE_WRITEBACK_CACHE != 0 142 143 // TODO(github.com/SagerNet/issue/3195): figure out how to use TimeGran (0 < TimeGran <= fuseMaxTimeGranNs). 144 145 if out.Flags&linux.FUSE_MAX_PAGES != 0 { 146 maxPages := out.MaxPages 147 if maxPages < 1 { 148 maxPages = 1 149 } 150 if maxPages > fuseMaxMaxPages { 151 maxPages = fuseMaxMaxPages 152 } 153 conn.maxPages = maxPages 154 } 155 } 156 157 // No support for limits before minor version 13. 158 if out.Minor >= 13 { 159 conn.asyncMu.Lock() 160 161 if out.MaxBackground > 0 { 162 conn.asyncNumMax = out.MaxBackground 163 164 if !hasSysAdminCap && 165 conn.asyncNumMax > MaxUserBackgroundRequest { 166 conn.asyncNumMax = MaxUserBackgroundRequest 167 } 168 } 169 170 if out.CongestionThreshold > 0 { 171 conn.asyncCongestionThreshold = out.CongestionThreshold 172 173 if !hasSysAdminCap && 174 conn.asyncCongestionThreshold > MaxUserCongestionThreshold { 175 conn.asyncCongestionThreshold = MaxUserCongestionThreshold 176 } 177 } 178 179 conn.asyncMu.Unlock() 180 } 181 182 return nil 183 } 184 185 // Abort this FUSE connection. 186 // It tries to acquire conn.fd.mu, conn.lock, conn.bgLock in order. 187 // All possible requests waiting or blocking will be aborted. 188 // 189 // Preconditions: conn.fd.mu is locked. 190 func (conn *connection) Abort(ctx context.Context) { 191 conn.mu.Lock() 192 conn.asyncMu.Lock() 193 194 if !conn.connected { 195 conn.asyncMu.Unlock() 196 conn.mu.Unlock() 197 return 198 } 199 200 conn.connected = false 201 202 // Empty the `fd.queue` that holds the requests 203 // not yet read by the FUSE daemon yet. 204 // These are a subset of the requests in `fuse.completion` map. 205 for !conn.fd.queue.Empty() { 206 req := conn.fd.queue.Front() 207 conn.fd.queue.Remove(req) 208 } 209 210 var terminate []linux.FUSEOpID 211 212 // 2. Collect the requests have not been sent to FUSE daemon, 213 // or have not received a reply. 214 for unique := range conn.fd.completions { 215 terminate = append(terminate, unique) 216 } 217 218 // Release locks to avoid deadlock. 219 conn.asyncMu.Unlock() 220 conn.mu.Unlock() 221 222 // 1. The requets blocked before initialization. 223 // Will reach call() `connected` check and return. 224 if !conn.Initialized() { 225 conn.SetInitialized() 226 } 227 228 // 2. Terminate the requests collected above. 229 // Set ECONNABORTED error. 230 // sendError() will remove them from `fd.completion` map. 231 // Will enter the path of a normally received error. 232 for _, toTerminate := range terminate { 233 conn.fd.sendError(ctx, -int32(unix.ECONNABORTED), toTerminate) 234 } 235 236 // 3. The requests not yet written to FUSE device. 237 // Early terminate. 238 // Will reach callFutureLocked() `connected` check and return. 239 close(conn.fd.fullQueueCh) 240 241 // TODO(github.com/SagerNet/issue/3528): Forget all pending forget reqs. 242 }