github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/fd/fd.go (about) 1 // Copyright 2018 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 fd provides types for working with file descriptors. 16 package fd 17 18 import ( 19 "fmt" 20 "io" 21 "os" 22 "runtime" 23 "sync/atomic" 24 25 "golang.org/x/sys/unix" 26 ) 27 28 // ReadWriter implements io.ReadWriter, io.ReaderAt, and io.WriterAt for fd. It 29 // does not take ownership of fd. 30 type ReadWriter struct { 31 // fd is accessed atomically so FD.Close/Release can swap it. 32 fd int64 33 } 34 35 var _ io.ReadWriter = (*ReadWriter)(nil) 36 var _ io.ReaderAt = (*ReadWriter)(nil) 37 var _ io.WriterAt = (*ReadWriter)(nil) 38 39 // NewReadWriter creates a ReadWriter for fd. 40 func NewReadWriter(fd int) *ReadWriter { 41 return &ReadWriter{int64(fd)} 42 } 43 44 func fixCount(n int, err error) (int, error) { 45 if n < 0 { 46 n = 0 47 } 48 return n, err 49 } 50 51 // Read implements io.Reader. 52 func (r *ReadWriter) Read(b []byte) (int, error) { 53 c, err := fixCount(unix.Read(r.FD(), b)) 54 if c == 0 && len(b) > 0 && err == nil { 55 return 0, io.EOF 56 } 57 return c, err 58 } 59 60 // ReadAt implements io.ReaderAt. 61 // 62 // ReadAt always returns a non-nil error when c < len(b). 63 func (r *ReadWriter) ReadAt(b []byte, off int64) (c int, err error) { 64 for len(b) > 0 { 65 var m int 66 m, err = fixCount(unix.Pread(r.FD(), b, off)) 67 if m == 0 && err == nil { 68 return c, io.EOF 69 } 70 if err != nil { 71 return c, err 72 } 73 c += m 74 b = b[m:] 75 off += int64(m) 76 } 77 return 78 } 79 80 // Write implements io.Writer. 81 func (r *ReadWriter) Write(b []byte) (int, error) { 82 var err error 83 var n, remaining int 84 for remaining = len(b); remaining > 0; { 85 woff := len(b) - remaining 86 n, err = unix.Write(r.FD(), b[woff:]) 87 88 if n > 0 { 89 // unix.Write wrote some bytes. This is the common case. 90 remaining -= n 91 } else { 92 if err == nil { 93 // unix.Write did not write anything nor did it return an error. 94 // 95 // There is no way to guarantee that a subsequent unix.Write will 96 // make forward progress so just panic. 97 panic(fmt.Sprintf("unix.Write returned %d with no error", n)) 98 } 99 100 if err != unix.EINTR { 101 // If the write failed for anything other than a signal, bail out. 102 break 103 } 104 } 105 } 106 107 return len(b) - remaining, err 108 } 109 110 // WriteAt implements io.WriterAt. 111 func (r *ReadWriter) WriteAt(b []byte, off int64) (c int, err error) { 112 for len(b) > 0 { 113 var m int 114 m, err = fixCount(unix.Pwrite(r.FD(), b, off)) 115 if err != nil { 116 break 117 } 118 c += m 119 b = b[m:] 120 off += int64(m) 121 } 122 return 123 } 124 125 // FD returns the owned file descriptor. Ownership remains unchanged. 126 func (r *ReadWriter) FD() int { 127 return int(atomic.LoadInt64(&r.fd)) 128 } 129 130 // String implements Stringer.String(). 131 func (r *ReadWriter) String() string { 132 return fmt.Sprintf("FD: %d", r.FD()) 133 } 134 135 // FD owns a host file descriptor. 136 // 137 // It is similar to os.File, with a few important distinctions: 138 // 139 // FD provies a Release() method which relinquishes ownership. Like os.File, 140 // FD adds a finalizer to close the backing FD. However, the finalizer cannot 141 // be removed from os.File, forever pinning the lifetime of an FD to its 142 // os.File. 143 // 144 // FD supports both blocking and non-blocking operation. os.File only 145 // supports blocking operation. 146 type FD struct { 147 ReadWriter 148 } 149 150 // New creates a new FD. 151 // 152 // New takes ownership of fd. 153 func New(fd int) *FD { 154 if fd < 0 { 155 return &FD{ReadWriter{-1}} 156 } 157 f := &FD{ReadWriter{int64(fd)}} 158 runtime.SetFinalizer(f, (*FD).Close) 159 return f 160 } 161 162 // NewFromFile creates a new FD from an os.File. 163 // 164 // NewFromFile does not transfer ownership of the file descriptor (it will be 165 // duplicated, so both the os.File and FD will eventually need to be closed 166 // and some (but not all) changes made to the FD will be applied to the 167 // os.File as well). 168 // 169 // The returned FD is always blocking (Go 1.9+). 170 func NewFromFile(file *os.File) (*FD, error) { 171 fd, err := unix.Dup(int(file.Fd())) 172 // Technically, the runtime may call the finalizer on file as soon as 173 // Fd() returns. 174 runtime.KeepAlive(file) 175 if err != nil { 176 return &FD{ReadWriter{-1}}, err 177 } 178 return New(fd), nil 179 } 180 181 // NewFromFiles creates new FDs for each file in the slice. 182 func NewFromFiles(files []*os.File) ([]*FD, error) { 183 rv := make([]*FD, 0, len(files)) 184 for _, f := range files { 185 new, err := NewFromFile(f) 186 if err != nil { 187 // Cleanup on error. 188 for _, fd := range rv { 189 fd.Close() 190 } 191 return nil, err 192 } 193 rv = append(rv, new) 194 } 195 return rv, nil 196 } 197 198 // Open is equivalent to open(2). 199 func Open(path string, openmode int, perm uint32) (*FD, error) { 200 f, err := unix.Open(path, openmode|unix.O_LARGEFILE, perm) 201 if err != nil { 202 return nil, err 203 } 204 return New(f), nil 205 } 206 207 // OpenAt is equivalent to openat(2). 208 func OpenAt(dir *FD, path string, flags int, mode uint32) (*FD, error) { 209 f, err := unix.Openat(dir.FD(), path, flags, mode) 210 if err != nil { 211 return nil, err 212 } 213 return New(f), nil 214 } 215 216 // Close closes the file descriptor contained in the FD. 217 // 218 // Close is safe to call multiple times, but will return an error after the 219 // first call. 220 // 221 // Concurrently calling Close and any other method is undefined. 222 func (f *FD) Close() error { 223 runtime.SetFinalizer(f, nil) 224 return unix.Close(int(atomic.SwapInt64(&f.fd, -1))) 225 } 226 227 // Release relinquishes ownership of the contained file descriptor. 228 // 229 // Concurrently calling Release and any other method is undefined. 230 func (f *FD) Release() int { 231 runtime.SetFinalizer(f, nil) 232 return int(atomic.SwapInt64(&f.fd, -1)) 233 } 234 235 // File converts the FD to an os.File. 236 // 237 // FD does not transfer ownership of the file descriptor (it will be 238 // duplicated, so both the FD and os.File will eventually need to be closed 239 // and some (but not all) changes made to the os.File will be applied to the 240 // FD as well). 241 // 242 // This operation is somewhat expensive, so care should be taken to minimize 243 // its use. 244 func (f *FD) File() (*os.File, error) { 245 fd, err := unix.Dup(f.FD()) 246 if err != nil { 247 return nil, err 248 } 249 return os.NewFile(uintptr(fd), ""), nil 250 } 251 252 // ReleaseToFile returns an os.File that takes ownership of the FD. 253 // 254 // name is passed to os.NewFile. 255 func (f *FD) ReleaseToFile(name string) *os.File { 256 return os.NewFile(uintptr(f.Release()), name) 257 }