// 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 host import ( "fmt" "syscall" "gvisor.dev/gvisor/pkg/fdnotifier" "gvisor.dev/gvisor/pkg/log" "gvisor.dev/gvisor/pkg/waiter" ) // descriptor wraps a host fd. // // +stateify savable type descriptor struct { // If origFD >= 0, it is the host fd that this file was originally created // from, which must be available at time of restore. The FD can be closed // after descriptor is created. origFD int // wouldBlock is true if value (below) points to a file that can // return EWOULDBLOCK for operations that would block. wouldBlock bool // value is the wrapped host fd. It is never saved or restored // directly. value int `state:"nosave"` } // newDescriptor returns a wrapped host file descriptor. On success, // the descriptor is registered for event notifications with queue. func newDescriptor(fd int, saveable bool, wouldBlock bool, queue *waiter.Queue) (*descriptor, error) { ownedFD := fd origFD := -1 if saveable { var err error ownedFD, err = syscall.Dup(fd) if err != nil { return nil, err } origFD = fd } if wouldBlock { if err := syscall.SetNonblock(ownedFD, true); err != nil { return nil, err } if err := fdnotifier.AddFD(int32(ownedFD), queue); err != nil { return nil, err } } return &descriptor{ origFD: origFD, wouldBlock: wouldBlock, value: ownedFD, }, nil } // initAfterLoad initializes the value of the descriptor after Load. func (d *descriptor) initAfterLoad(id uint64, queue *waiter.Queue) error { var err error d.value, err = syscall.Dup(d.origFD) if err != nil { return fmt.Errorf("failed to dup restored fd %d: %v", d.origFD, err) } if d.wouldBlock { if err := syscall.SetNonblock(d.value, true); err != nil { return err } if err := fdnotifier.AddFD(int32(d.value), queue); err != nil { return err } } return nil } // Release releases all resources held by descriptor. func (d *descriptor) Release() { if d.wouldBlock { fdnotifier.RemoveFD(int32(d.value)) } if err := syscall.Close(d.value); err != nil { log.Warningf("error closing fd %d: %v", d.value, err) } d.value = -1 }