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