1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
|
// Copyright 2018 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 p9
import (
"io"
"runtime/debug"
"golang.org/x/sys/unix"
"gvisor.dev/gvisor/pkg/atomicbitops"
"gvisor.dev/gvisor/pkg/errors/linuxerr"
"gvisor.dev/gvisor/pkg/fd"
"gvisor.dev/gvisor/pkg/fdchannel"
"gvisor.dev/gvisor/pkg/flipcall"
"gvisor.dev/gvisor/pkg/log"
"gvisor.dev/gvisor/pkg/sync"
"gvisor.dev/gvisor/pkg/unet"
)
// Server is a 9p2000.L server.
type Server struct {
// attacher provides the attach function.
attacher Attacher
options AttacherOptions
// pathTree is the full set of paths opened on this server.
//
// These may be across different connections, but rename operations
// must be serialized globally for safely. There is a single pathTree
// for the entire server, and not per connection.
pathTree *pathNode
// renameMu is a global lock protecting rename operations. With this
// lock, we can be certain that any given rename operation can safely
// acquire two path nodes in any order, as all other concurrent
// operations acquire at most a single node.
renameMu sync.RWMutex
}
// NewServer returns a new server. attacher may be nil.
func NewServer(attacher Attacher) *Server {
opts := AttacherOptions{}
if attacher != nil {
opts = attacher.ServerOptions()
}
return &Server{
attacher: attacher,
options: opts,
pathTree: newPathNode(),
}
}
// connState is the state for a single connection.
type connState struct {
// server is the backing server.
server *Server
// fids is the set of active FIDs.
//
// This is used to find FIDs for files.
fidMu sync.Mutex
fids map[FID]*fidRef
// tags is the set of active tags.
//
// The given channel is closed when the
// tag is finished with processing.
tagMu sync.Mutex
tags map[Tag]chan struct{}
// messageSize is the maximum message size. The server does not
// do automatic splitting of messages.
messageSize atomicbitops.Uint32
// version is the agreed upon version X of 9P2000.L.Google.X.
// version 0 implies 9P2000.L.
version atomicbitops.Uint32
// reqGate counts requests that are still being handled.
reqGate sync.Gate
// -- below relates to the legacy handler --
// recvMu serializes receiving from conn.
recvMu sync.Mutex
// recvIdle is the number of goroutines in handleRequests() attempting to
// lock recvMu so that they can receive from conn.
recvIdle atomicbitops.Int32
// If recvShutdown is true, at least one goroutine has observed a
// connection error while receiving from conn, and all goroutines in
// handleRequests() should exit immediately. recvShutdown is protected by
// recvMu.
recvShutdown bool
// sendMu serializes sending to conn.
sendMu sync.Mutex
// conn is the connection used by the legacy transport.
conn *unet.Socket
// -- below relates to the flipcall handler --
// channelMu protects below.
channelMu sync.Mutex
// channelWg represents active workers.
channelWg sync.WaitGroup
// channelAlloc allocates channel memory.
channelAlloc *flipcall.PacketWindowAllocator
// channels are the set of initialized channels.
channels []*channel
}
// fidRef wraps a node and tracks references.
type fidRef struct {
// server is the associated server.
server *Server
// file is the associated File.
file File
// refs is an active refence count.
//
// The node above will be closed only when refs reaches zero.
refs atomicbitops.Int64
// opened indicates whether this has been opened already.
//
// This is updated in handlers.go.
//
// opened is protected by pathNode.opMu or renameMu (for write).
opened bool
// mode is the fidRef's mode from the walk. Only the type bits are
// valid, the permissions may change. This is used to sanity check
// operations on this element, and prevent walks across
// non-directories.
mode FileMode
// openFlags is the mode used in the open.
//
// This is updated in handlers.go.
//
// openFlags is protected by pathNode.opMu or renameMu (for write).
openFlags OpenFlags
// pathNode is the current pathNode for this FID.
pathNode *pathNode
// parent is the parent fidRef. We hold on to a parent reference to
// ensure that hooks, such as Renamed, can be executed safely by the
// server code.
//
// Note that parent cannot be changed without holding both the global
// rename lock and a writable lock on the associated pathNode for this
// fidRef. Holding either of these locks is sufficient to examine
// parent safely.
//
// The parent will be nil for root fidRefs, and non-nil otherwise. The
// method maybeParent can be used to return a cyclical reference, and
// isRoot should be used to check for root over looking at parent
// directly.
parent *fidRef
}
// IncRef increases the references on a fid.
func (f *fidRef) IncRef() {
f.refs.Add(1)
}
// DecRef should be called when you're finished with a fid.
func (f *fidRef) DecRef() {
if f.refs.Add(-1) == 0 {
f.file.Close()
// Drop the parent reference.
//
// Since this fidRef is guaranteed to be non-discoverable when
// the references reach zero, we don't need to worry about
// clearing the parent.
if f.parent != nil {
// If we've been previously deleted, this removing this
// ref is a no-op. That's expected.
f.parent.pathNode.removeChild(f)
f.parent.DecRef()
}
}
}
// TryIncRef returns true if a new reference is taken on the fid, and false if
// the fid has been destroyed.
func (f *fidRef) TryIncRef() bool {
for {
r := f.refs.Load()
if r <= 0 {
return false
}
if f.refs.CompareAndSwap(r, r+1) {
return true
}
}
}
// isDeleted returns true if this fidRef has been deleted.
//
// Precondition: this must be called via safelyRead, safelyWrite or
// safelyGlobal.
func (f *fidRef) isDeleted() bool {
return f.pathNode.deleted.Load() != 0
}
// isRoot indicates whether this is a root fid.
func (f *fidRef) isRoot() bool {
return f.parent == nil
}
// maybeParent returns a cyclic reference for roots, and the parent otherwise.
func (f *fidRef) maybeParent() *fidRef {
if f.parent != nil {
return f.parent
}
return f // Root has itself.
}
// notifyDelete marks all fidRefs as deleted.
//
// Precondition: this must be called via safelyWrite or safelyGlobal.
func notifyDelete(pn *pathNode) {
pn.deleted.Store(1)
// Call on all subtrees.
pn.forEachChildNode(func(pn *pathNode) {
notifyDelete(pn)
})
}
// markChildDeleted marks all children below the given name as deleted.
//
// Precondition: this must be called via safelyWrite or safelyGlobal.
func (f *fidRef) markChildDeleted(name string) {
if origPathNode := f.pathNode.removeWithName(name, nil); origPathNode != nil {
// Mark all children as deleted.
notifyDelete(origPathNode)
}
}
// notifyNameChange calls the relevant Renamed method on all nodes in the path,
// recursively. Note that this applies only for subtrees, as these
// notifications do not apply to the actual file whose name has changed.
//
// Precondition: this must be called via safelyGlobal.
func notifyNameChange(pn *pathNode) {
// Call on all local references.
pn.forEachChildRef(func(ref *fidRef, name string) {
ref.file.Renamed(ref.parent.file, name)
})
// Call on all subtrees.
pn.forEachChildNode(func(pn *pathNode) {
notifyNameChange(pn)
})
}
// renameChildTo renames the given child to the target.
//
// Precondition: this must be called via safelyGlobal.
func (f *fidRef) renameChildTo(oldName string, target *fidRef, newName string) {
target.markChildDeleted(newName)
origPathNode := f.pathNode.removeWithName(oldName, func(ref *fidRef) {
// N.B. DecRef can take f.pathNode's parent's childMu. This is
// allowed because renameMu is held for write via safelyGlobal.
ref.parent.DecRef() // Drop original reference.
ref.parent = target // Change parent.
ref.parent.IncRef() // Acquire new one.
if f.pathNode == target.pathNode {
target.pathNode.addChildLocked(ref, newName)
} else {
target.pathNode.addChild(ref, newName)
}
ref.file.Renamed(target.file, newName)
})
if origPathNode != nil {
// Replace the previous (now deleted) path node.
target.pathNode.addPathNodeFor(newName, origPathNode)
// Call Renamed on all children.
notifyNameChange(origPathNode)
}
}
// safelyRead executes the given operation with the local path node locked.
// This implies that paths will not change during the operation.
func (f *fidRef) safelyRead(fn func() error) (err error) {
f.server.renameMu.RLock()
defer f.server.renameMu.RUnlock()
f.pathNode.opMu.RLock()
defer f.pathNode.opMu.RUnlock()
return fn()
}
// safelyWrite executes the given operation with the local path node locked in
// a writable fashion. This implies some paths may change.
func (f *fidRef) safelyWrite(fn func() error) (err error) {
f.server.renameMu.RLock()
defer f.server.renameMu.RUnlock()
f.pathNode.opMu.Lock()
defer f.pathNode.opMu.Unlock()
return fn()
}
// safelyGlobal executes the given operation with the global path lock held.
func (f *fidRef) safelyGlobal(fn func() error) (err error) {
f.server.renameMu.Lock()
defer f.server.renameMu.Unlock()
return fn()
}
// LookupFID finds the given FID.
//
// You should call fid.DecRef when you are finished using the fid.
func (cs *connState) LookupFID(fid FID) (*fidRef, bool) {
cs.fidMu.Lock()
defer cs.fidMu.Unlock()
fidRef, ok := cs.fids[fid]
if ok {
fidRef.IncRef()
return fidRef, true
}
return nil, false
}
// InsertFID installs the given FID.
//
// This fid starts with a reference count of one. If a FID exists in
// the slot already it is closed, per the specification.
func (cs *connState) InsertFID(fid FID, newRef *fidRef) {
cs.fidMu.Lock()
defer cs.fidMu.Unlock()
origRef, ok := cs.fids[fid]
if ok {
defer origRef.DecRef()
}
newRef.IncRef()
cs.fids[fid] = newRef
}
// DeleteFID removes the given FID.
//
// This simply removes it from the map and drops a reference.
func (cs *connState) DeleteFID(fid FID) bool {
cs.fidMu.Lock()
defer cs.fidMu.Unlock()
fidRef, ok := cs.fids[fid]
if !ok {
return false
}
delete(cs.fids, fid)
fidRef.DecRef()
return true
}
// StartTag starts handling the tag.
//
// False is returned if this tag is already active.
func (cs *connState) StartTag(t Tag) bool {
cs.tagMu.Lock()
defer cs.tagMu.Unlock()
_, ok := cs.tags[t]
if ok {
return false
}
cs.tags[t] = make(chan struct{})
return true
}
// ClearTag finishes handling a tag.
func (cs *connState) ClearTag(t Tag) {
cs.tagMu.Lock()
defer cs.tagMu.Unlock()
ch, ok := cs.tags[t]
if !ok {
// Should never happen.
panic("unused tag cleared")
}
delete(cs.tags, t)
// Notify.
close(ch)
}
// WaitTag waits for a tag to finish.
func (cs *connState) WaitTag(t Tag) {
cs.tagMu.Lock()
ch, ok := cs.tags[t]
cs.tagMu.Unlock()
if !ok {
return
}
// Wait for close.
<-ch
}
// initializeChannels initializes all channels.
//
// This is a no-op if channels are already initialized.
func (cs *connState) initializeChannels() (err error) {
cs.channelMu.Lock()
defer cs.channelMu.Unlock()
// Initialize our channel allocator.
if cs.channelAlloc == nil {
alloc, err := flipcall.NewPacketWindowAllocator()
if err != nil {
return err
}
cs.channelAlloc = alloc
}
// Create all the channels.
for len(cs.channels) < channelsPerClient {
res := &channel{
done: make(chan struct{}),
}
res.desc, err = cs.channelAlloc.Allocate(channelSize)
if err != nil {
return err
}
if err := res.data.Init(flipcall.ServerSide, res.desc); err != nil {
return err
}
socks, err := fdchannel.NewConnectedSockets()
if err != nil {
res.data.Destroy() // Cleanup.
return err
}
res.fds.Init(socks[0])
res.client = fd.New(socks[1])
cs.channels = append(cs.channels, res)
// Start servicing the channel.
//
// When we call stop, we will close all the channels and these
// routines should finish. We need the wait group to ensure
// that active handlers are actually finished before cleanup.
cs.channelWg.Add(1)
go func() { // S/R-SAFE: Server side.
defer cs.channelWg.Done()
if err := res.service(cs); err != nil {
// Don't log flipcall.ShutdownErrors, which we expect to be
// returned during server shutdown.
if _, ok := err.(flipcall.ShutdownError); !ok {
log.Warningf("p9.channel.service: %v", err)
}
}
}()
}
return nil
}
// lookupChannel looks up the channel with given id.
//
// The function returns nil if no such channel is available.
func (cs *connState) lookupChannel(id uint32) *channel {
cs.channelMu.Lock()
defer cs.channelMu.Unlock()
if id >= uint32(len(cs.channels)) {
return nil
}
return cs.channels[id]
}
// handle handles a single message.
func (cs *connState) handle(m message) (r message) {
if !cs.reqGate.Enter() {
// connState.stop() has been called; the connection is shutting down.
r = newErrFromLinuxerr(linuxerr.ECONNRESET)
return
}
defer func() {
cs.reqGate.Leave()
if r == nil {
// Don't allow a panic to propagate.
err := recover()
// Include a useful log message.
log.Warningf("panic in handler: %v\n%s", err, debug.Stack())
// Wrap in an EREMOTEIO error; we don't really have a
// better way to describe this kind of error. It will
// usually manifest as a result of the test framework.
r = newErrFromLinuxerr(linuxerr.EREMOTEIO)
}
}()
if handler, ok := m.(handler); ok {
// Call the message handler.
r = handler.handle(cs)
// TODO(b/34162363):This is only here to make sure the server works with
// only linuxerr Errors, as the handlers work with both client and server.
// It will be removed a followup, when all the unix.Errno errors are
// replaced with linuxerr.
if rlError, ok := r.(*Rlerror); ok {
e := linuxerr.ErrorFromUnix(unix.Errno(rlError.Error))
r = newErrFromLinuxerr(e)
}
} else {
// Produce an ENOSYS error.
r = newErrFromLinuxerr(linuxerr.ENOSYS)
}
return
}
// handleRequest handles a single request. It returns true if the caller should
// continue handling requests and false if it should terminate.
func (cs *connState) handleRequest() bool {
// Obtain the right to receive a message from cs.conn.
cs.recvIdle.Add(1)
cs.recvMu.Lock()
cs.recvIdle.Add(-1)
if cs.recvShutdown {
// Another goroutine already detected a connection problem; exit
// immediately.
cs.recvMu.Unlock()
return false
}
messageSize := cs.messageSize.Load()
if messageSize == 0 {
// Default or not yet negotiated.
messageSize = maximumLength
}
// Receive a message.
tag, m, err := recv(cs.conn, messageSize, msgRegistry.get)
if errSocket, ok := err.(ErrSocket); ok {
// Connection problem; stop serving.
log.Debugf("p9.recv: %v", errSocket.error)
cs.recvShutdown = true
cs.recvMu.Unlock()
return false
}
// Ensure that another goroutine is available to receive from cs.conn.
if cs.recvIdle.Load() == 0 {
go cs.handleRequests() // S/R-SAFE: Irrelevant.
}
cs.recvMu.Unlock()
// Deal with other errors.
if err != nil && err != io.EOF {
// If it's not a connection error, but some other protocol error,
// we can send a response immediately.
cs.sendMu.Lock()
err := send(cs.conn, tag, newErrFromLinuxerr(err))
cs.sendMu.Unlock()
if err != nil {
log.Debugf("p9.send: %v", err)
}
return true
}
// Try to start the tag.
if !cs.StartTag(tag) {
// Nothing we can do at this point; client is bogus.
log.Debugf("no valid tag [%05d]", tag)
return true
}
// Handle the message.
r := cs.handle(m)
// Clear the tag before sending. That's because as soon as this hits
// the wire, the client can legally send the same tag.
cs.ClearTag(tag)
// Send back the result.
cs.sendMu.Lock()
err = send(cs.conn, tag, r)
cs.sendMu.Unlock()
if err != nil {
log.Debugf("p9.send: %v", err)
}
// Return the message to the cache.
msgRegistry.put(m)
return true
}
func (cs *connState) handleRequests() {
for {
if !cs.handleRequest() {
return
}
}
}
func (cs *connState) stop() {
// Stop new requests from proceeding, and wait for completion of all
// inflight requests. This is mostly so that if a request is stuck, the
// sandbox supervisor has the opportunity to kill us with SIGABRT to get a
// stack dump of the offending handler.
cs.reqGate.Close()
// Free the channels.
cs.channelMu.Lock()
for _, ch := range cs.channels {
ch.Shutdown()
}
cs.channelWg.Wait()
for _, ch := range cs.channels {
ch.Close()
}
cs.channels = nil // Clear.
cs.channelMu.Unlock()
// Free the channel memory.
if cs.channelAlloc != nil {
cs.channelAlloc.Destroy()
}
// Ensure the connection is closed.
cs.conn.Close()
// Close all remaining fids.
for fid, fidRef := range cs.fids {
delete(cs.fids, fid)
// Drop final reference in the FID table. Note this should
// always close the file, since we've ensured that there are no
// handlers running via the wait for Pending => 0 below.
fidRef.DecRef()
}
}
// Handle handles a single connection.
func (s *Server) Handle(conn *unet.Socket) error {
cs := &connState{
server: s,
fids: make(map[FID]*fidRef),
tags: make(map[Tag]chan struct{}),
conn: conn,
}
defer cs.stop()
// Serve requests from conn in the current goroutine; handleRequests() will
// create more goroutines as needed.
cs.handleRequests()
return nil
}
// Serve handles requests from the bound socket.
//
// The passed serverSocket _must_ be created in packet mode.
func (s *Server) Serve(serverSocket *unet.ServerSocket) error {
var wg sync.WaitGroup
defer wg.Wait()
for {
conn, err := serverSocket.Accept()
if err != nil {
// Something went wrong.
//
// Socket closed?
return err
}
wg.Add(1)
go func(conn *unet.Socket) { // S/R-SAFE: Irrelevant.
s.Handle(conn)
wg.Done()
}(conn)
}
}
|