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 }