github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/socket/control/control_vfs2.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 control
    16  
    17  import (
    18  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    19  	"github.com/SagerNet/gvisor/pkg/context"
    20  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    21  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    22  	"github.com/SagerNet/gvisor/pkg/sentry/socket/unix/transport"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    24  )
    25  
    26  // SCMRightsVFS2 represents a SCM_RIGHTS socket control message.
    27  //
    28  // +stateify savable
    29  type SCMRightsVFS2 interface {
    30  	transport.RightsControlMessage
    31  
    32  	// Files returns up to max RightsFiles.
    33  	//
    34  	// Returned files are consumed and ownership is transferred to the caller.
    35  	// Subsequent calls to Files will return the next files.
    36  	Files(ctx context.Context, max int) (rf RightsFilesVFS2, truncated bool)
    37  }
    38  
    39  // RightsFilesVFS2 represents a SCM_RIGHTS socket control message. A reference
    40  // is maintained for each vfs.FileDescription and is release either when an FD
    41  // is created or when the Release method is called.
    42  //
    43  // +stateify savable
    44  type RightsFilesVFS2 []*vfs.FileDescription
    45  
    46  // NewSCMRightsVFS2 creates a new SCM_RIGHTS socket control message
    47  // representation using local sentry FDs.
    48  func NewSCMRightsVFS2(t *kernel.Task, fds []int32) (SCMRightsVFS2, error) {
    49  	files := make(RightsFilesVFS2, 0, len(fds))
    50  	for _, fd := range fds {
    51  		file := t.GetFileVFS2(fd)
    52  		if file == nil {
    53  			files.Release(t)
    54  			return nil, linuxerr.EBADF
    55  		}
    56  		files = append(files, file)
    57  	}
    58  	return &files, nil
    59  }
    60  
    61  // Files implements SCMRights.Files.
    62  func (fs *RightsFilesVFS2) Files(ctx context.Context, max int) (RightsFilesVFS2, bool) {
    63  	n := max
    64  	var trunc bool
    65  	if l := len(*fs); n > l {
    66  		n = l
    67  	} else if n < l {
    68  		trunc = true
    69  	}
    70  	rf := (*fs)[:n]
    71  	*fs = (*fs)[n:]
    72  	return rf, trunc
    73  }
    74  
    75  // Clone implements transport.RightsControlMessage.Clone.
    76  func (fs *RightsFilesVFS2) Clone() transport.RightsControlMessage {
    77  	nfs := append(RightsFilesVFS2(nil), *fs...)
    78  	for _, nf := range nfs {
    79  		nf.IncRef()
    80  	}
    81  	return &nfs
    82  }
    83  
    84  // Release implements transport.RightsControlMessage.Release.
    85  func (fs *RightsFilesVFS2) Release(ctx context.Context) {
    86  	for _, f := range *fs {
    87  		f.DecRef(ctx)
    88  	}
    89  	*fs = nil
    90  }
    91  
    92  // rightsFDsVFS2 gets up to the specified maximum number of FDs.
    93  func rightsFDsVFS2(t *kernel.Task, rights SCMRightsVFS2, cloexec bool, max int) ([]int32, bool) {
    94  	files, trunc := rights.Files(t, max)
    95  	fds := make([]int32, 0, len(files))
    96  	for i := 0; i < max && len(files) > 0; i++ {
    97  		fd, err := t.NewFDFromVFS2(0, files[0], kernel.FDFlags{
    98  			CloseOnExec: cloexec,
    99  		})
   100  		files[0].DecRef(t)
   101  		files = files[1:]
   102  		if err != nil {
   103  			t.Warningf("Error inserting FD: %v", err)
   104  			// This is what Linux does.
   105  			break
   106  		}
   107  
   108  		fds = append(fds, int32(fd))
   109  	}
   110  	return fds, trunc
   111  }
   112  
   113  // PackRightsVFS2 packs as many FDs as will fit into the unused capacity of buf.
   114  func PackRightsVFS2(t *kernel.Task, rights SCMRightsVFS2, cloexec bool, buf []byte, flags int) ([]byte, int) {
   115  	maxFDs := (cap(buf) - len(buf) - linux.SizeOfControlMessageHeader) / 4
   116  	// Linux does not return any FDs if none fit.
   117  	if maxFDs <= 0 {
   118  		flags |= linux.MSG_CTRUNC
   119  		return buf, flags
   120  	}
   121  	fds, trunc := rightsFDsVFS2(t, rights, cloexec, maxFDs)
   122  	if trunc {
   123  		flags |= linux.MSG_CTRUNC
   124  	}
   125  	align := t.Arch().Width()
   126  	return putCmsg(buf, flags, linux.SCM_RIGHTS, align, fds)
   127  }
   128  
   129  // NewVFS2 creates default control messages if needed.
   130  func NewVFS2(t *kernel.Task, socketOrEndpoint interface{}, rights SCMRightsVFS2) transport.ControlMessages {
   131  	return transport.ControlMessages{
   132  		Credentials: makeCreds(t, socketOrEndpoint),
   133  		Rights:      rights,
   134  	}
   135  }