gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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  	"gvisor.dev/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  }