github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/fdimport/fdimport.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 fdimport provides the Import function.
    16  package fdimport
    17  
    18  import (
    19  	"fmt"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/context"
    22  	"github.com/SagerNet/gvisor/pkg/fd"
    23  	"github.com/SagerNet/gvisor/pkg/sentry/fs"
    24  	"github.com/SagerNet/gvisor/pkg/sentry/fs/host"
    25  	hostvfs2 "github.com/SagerNet/gvisor/pkg/sentry/fsimpl/host"
    26  	"github.com/SagerNet/gvisor/pkg/sentry/kernel"
    27  	"github.com/SagerNet/gvisor/pkg/sentry/vfs"
    28  )
    29  
    30  // Import imports a slice of FDs into the given FDTable. If console is true,
    31  // sets up TTY for the first 3 FDs in the slice representing stdin, stdout,
    32  // stderr. Used FDs are either closed or released. It's safe for the caller to
    33  // close any remaining files upon return.
    34  func Import(ctx context.Context, fdTable *kernel.FDTable, console bool, fds []*fd.FD) (*host.TTYFileOperations, *hostvfs2.TTYFileDescription, error) {
    35  	if kernel.VFS2Enabled {
    36  		ttyFile, err := importVFS2(ctx, fdTable, console, fds)
    37  		return nil, ttyFile, err
    38  	}
    39  	ttyFile, err := importFS(ctx, fdTable, console, fds)
    40  	return ttyFile, nil, err
    41  }
    42  
    43  func importFS(ctx context.Context, fdTable *kernel.FDTable, console bool, fds []*fd.FD) (*host.TTYFileOperations, error) {
    44  	var ttyFile *fs.File
    45  	for appFD, hostFD := range fds {
    46  		var appFile *fs.File
    47  
    48  		if console && appFD < 3 {
    49  			// Import the file as a host TTY file.
    50  			if ttyFile == nil {
    51  				var err error
    52  				appFile, err = host.ImportFile(ctx, hostFD.FD(), true /* isTTY */)
    53  				if err != nil {
    54  					return nil, err
    55  				}
    56  				defer appFile.DecRef(ctx)
    57  				_ = hostFD.Close() // FD is dup'd i ImportFile.
    58  
    59  				// Remember this in the TTY file, as we will
    60  				// use it for the other stdio FDs.
    61  				ttyFile = appFile
    62  			} else {
    63  				// Re-use the existing TTY file, as all three
    64  				// stdio FDs must point to the same fs.File in
    65  				// order to share TTY state, specifically the
    66  				// foreground process group id.
    67  				appFile = ttyFile
    68  			}
    69  		} else {
    70  			// Import the file as a regular host file.
    71  			var err error
    72  			appFile, err = host.ImportFile(ctx, hostFD.FD(), false /* isTTY */)
    73  			if err != nil {
    74  				return nil, err
    75  			}
    76  			defer appFile.DecRef(ctx)
    77  			_ = hostFD.Close() // FD is dup'd i ImportFile.
    78  		}
    79  
    80  		// Add the file to the FD map.
    81  		if err := fdTable.NewFDAt(ctx, int32(appFD), appFile, kernel.FDFlags{}); err != nil {
    82  			return nil, err
    83  		}
    84  	}
    85  
    86  	if ttyFile == nil {
    87  		return nil, nil
    88  	}
    89  	return ttyFile.FileOperations.(*host.TTYFileOperations), nil
    90  }
    91  
    92  func importVFS2(ctx context.Context, fdTable *kernel.FDTable, console bool, stdioFDs []*fd.FD) (*hostvfs2.TTYFileDescription, error) {
    93  	k := kernel.KernelFromContext(ctx)
    94  	if k == nil {
    95  		return nil, fmt.Errorf("cannot find kernel from context")
    96  	}
    97  
    98  	var ttyFile *vfs.FileDescription
    99  	for appFD, hostFD := range stdioFDs {
   100  		var appFile *vfs.FileDescription
   101  
   102  		if console && appFD < 3 {
   103  			// Import the file as a host TTY file.
   104  			if ttyFile == nil {
   105  				var err error
   106  				appFile, err = hostvfs2.ImportFD(ctx, k.HostMount(), hostFD.FD(), true /* isTTY */)
   107  				if err != nil {
   108  					return nil, err
   109  				}
   110  				defer appFile.DecRef(ctx)
   111  				hostFD.Release() // FD is transfered to host FD.
   112  
   113  				// Remember this in the TTY file, as we will use it for the other stdio
   114  				// FDs.
   115  				ttyFile = appFile
   116  			} else {
   117  				// Re-use the existing TTY file, as all three stdio FDs must point to
   118  				// the same fs.File in order to share TTY state, specifically the
   119  				// foreground process group id.
   120  				appFile = ttyFile
   121  			}
   122  		} else {
   123  			var err error
   124  			appFile, err = hostvfs2.ImportFD(ctx, k.HostMount(), hostFD.FD(), false /* isTTY */)
   125  			if err != nil {
   126  				return nil, err
   127  			}
   128  			defer appFile.DecRef(ctx)
   129  			hostFD.Release() // FD is transfered to host FD.
   130  		}
   131  
   132  		if err := fdTable.NewFDAtVFS2(ctx, int32(appFD), appFile, kernel.FDFlags{}); err != nil {
   133  			return nil, err
   134  		}
   135  	}
   136  
   137  	if ttyFile == nil {
   138  		return nil, nil
   139  	}
   140  	return ttyFile.Impl().(*hostvfs2.TTYFileDescription), nil
   141  }