gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/runsc/fsgofer/lisafs.go (about)

     1  // Copyright 2021 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 fsgofer provides a lisafs server implementation which gives access
    16  // to local files.
    17  package fsgofer
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"math"
    23  	"os"
    24  	"path"
    25  	"path/filepath"
    26  	"strconv"
    27  	"sync"
    28  
    29  	"golang.org/x/sys/unix"
    30  	"gvisor.dev/gvisor/pkg/abi/linux"
    31  	"gvisor.dev/gvisor/pkg/atomicbitops"
    32  	"gvisor.dev/gvisor/pkg/cleanup"
    33  	rwfd "gvisor.dev/gvisor/pkg/fd"
    34  	"gvisor.dev/gvisor/pkg/fsutil"
    35  	"gvisor.dev/gvisor/pkg/lisafs"
    36  	"gvisor.dev/gvisor/pkg/log"
    37  	"gvisor.dev/gvisor/pkg/marshal/primitive"
    38  	"gvisor.dev/gvisor/runsc/config"
    39  )
    40  
    41  // LINT.IfChange
    42  
    43  const (
    44  	openFlags = unix.O_NOFOLLOW | unix.O_CLOEXEC
    45  
    46  	// UNIX_PATH_MAX as defined in include/uapi/linux/un.h.
    47  	unixPathMax = 108
    48  )
    49  
    50  // Config sets configuration options for each attach point.
    51  type Config struct {
    52  	// ROMount is set to true if this is a readonly mount.
    53  	ROMount bool
    54  
    55  	// PanicOnWrite panics on attempts to write to RO mounts.
    56  	PanicOnWrite bool
    57  
    58  	// HostUDS signals whether the gofer can connect to host unix domain sockets.
    59  	HostUDS config.HostUDS
    60  
    61  	// HostFifo signals whether the gofer can connect to host FIFOs.
    62  	HostFifo config.HostFifo
    63  
    64  	// DonateMountPointFD indicates whether a host FD to the mount point should
    65  	// be donated to the client on Mount RPC.
    66  	DonateMountPointFD bool
    67  }
    68  
    69  var procSelfFD *rwfd.FD
    70  
    71  // OpenProcSelfFD opens the /proc/self/fd directory, which will be used to
    72  // reopen file descriptors.
    73  func OpenProcSelfFD(path string) error {
    74  	d, err := unix.Open(path, unix.O_RDONLY|unix.O_DIRECTORY, 0)
    75  	if err != nil {
    76  		return fmt.Errorf("error opening /proc/self/fd: %v", err)
    77  	}
    78  	procSelfFD = rwfd.New(d)
    79  	return nil
    80  }
    81  
    82  // LisafsServer implements lisafs.ServerImpl for fsgofer.
    83  type LisafsServer struct {
    84  	lisafs.Server
    85  	config Config
    86  }
    87  
    88  var _ lisafs.ServerImpl = (*LisafsServer)(nil)
    89  
    90  // NewLisafsServer initializes a new lisafs server for fsgofer.
    91  func NewLisafsServer(config Config) *LisafsServer {
    92  	s := &LisafsServer{config: config}
    93  	s.Server.Init(s, lisafs.ServerOpts{
    94  		WalkStatSupported: true,
    95  		SetAttrOnDeleted:  true,
    96  		AllocateOnDeleted: true,
    97  	})
    98  	return s
    99  }
   100  
   101  // Mount implements lisafs.ServerImpl.Mount.
   102  func (s *LisafsServer) Mount(c *lisafs.Connection, mountNode *lisafs.Node) (*lisafs.ControlFD, linux.Statx, int, error) {
   103  	mountPath := mountNode.FilePath()
   104  	rootHostFD, err := tryOpen(func(flags int) (int, error) {
   105  		return unix.Open(mountPath, flags, 0)
   106  	})
   107  	if err != nil {
   108  		return nil, linux.Statx{}, -1, err
   109  	}
   110  	cu := cleanup.Make(func() {
   111  		_ = unix.Close(rootHostFD)
   112  	})
   113  	defer cu.Clean()
   114  
   115  	stat, err := fstatTo(rootHostFD)
   116  	if err != nil {
   117  		return nil, linux.Statx{}, -1, err
   118  	}
   119  
   120  	if err := checkSupportedFileType(uint32(stat.Mode)); err != nil {
   121  		log.Warningf("Mount: checkSupportedFileType() failed for file %q with mode %o: %v", mountPath, stat.Mode, err)
   122  		return nil, linux.Statx{}, -1, err
   123  	}
   124  
   125  	clientHostFD := -1
   126  	if s.config.DonateMountPointFD {
   127  		clientHostFD, err = unix.Dup(rootHostFD)
   128  		if err != nil {
   129  			return nil, linux.Statx{}, -1, err
   130  		}
   131  	}
   132  	cu.Release()
   133  
   134  	rootFD := &controlFDLisa{
   135  		hostFD:         rootHostFD,
   136  		writableHostFD: atomicbitops.FromInt32(-1),
   137  		isMountPoint:   true,
   138  	}
   139  	mountNode.IncRef() // Ref is transferred to ControlFD.
   140  	rootFD.ControlFD.Init(c, mountNode, linux.FileMode(stat.Mode), rootFD)
   141  	return rootFD.FD(), stat, clientHostFD, nil
   142  }
   143  
   144  // MaxMessageSize implements lisafs.ServerImpl.MaxMessageSize.
   145  func (s *LisafsServer) MaxMessageSize() uint32 {
   146  	return lisafs.MaxMessageSize()
   147  }
   148  
   149  // SupportedMessages implements lisafs.ServerImpl.SupportedMessages.
   150  func (s *LisafsServer) SupportedMessages() []lisafs.MID {
   151  	// Note that Flush, FListXattr and FRemoveXattr are not supported.
   152  	return []lisafs.MID{
   153  		lisafs.Mount,
   154  		lisafs.Channel,
   155  		lisafs.FStat,
   156  		lisafs.SetStat,
   157  		lisafs.Walk,
   158  		lisafs.WalkStat,
   159  		lisafs.OpenAt,
   160  		lisafs.OpenCreateAt,
   161  		lisafs.Close,
   162  		lisafs.FSync,
   163  		lisafs.PWrite,
   164  		lisafs.PRead,
   165  		lisafs.MkdirAt,
   166  		lisafs.MknodAt,
   167  		lisafs.SymlinkAt,
   168  		lisafs.LinkAt,
   169  		lisafs.FStatFS,
   170  		lisafs.FAllocate,
   171  		lisafs.ReadLinkAt,
   172  		lisafs.Connect,
   173  		lisafs.UnlinkAt,
   174  		lisafs.RenameAt,
   175  		lisafs.Getdents64,
   176  		lisafs.FGetXattr,
   177  		lisafs.FSetXattr,
   178  		lisafs.BindAt,
   179  		lisafs.Listen,
   180  		lisafs.Accept,
   181  	}
   182  }
   183  
   184  // controlFDLisa implements lisafs.ControlFDImpl.
   185  type controlFDLisa struct {
   186  	lisafs.ControlFD
   187  
   188  	// hostFD is the file descriptor which can be used to make host syscalls.
   189  	hostFD int
   190  
   191  	// writableHostFD is the file descriptor number for a writable FD opened on
   192  	// the same FD as `hostFD`. It is initialized to -1, and can change in value
   193  	// exactly once.
   194  	writableHostFD atomicbitops.Int32
   195  
   196  	// isMountpoint indicates whether this FD represents the mount point for its
   197  	// owning connection. isMountPoint is immutable.
   198  	isMountPoint bool
   199  }
   200  
   201  var _ lisafs.ControlFDImpl = (*controlFDLisa)(nil)
   202  
   203  func newControlFDLisa(hostFD int, parent *controlFDLisa, name string, mode linux.FileMode) *controlFDLisa {
   204  	var (
   205  		childFD    *controlFDLisa
   206  		childNode  *lisafs.Node
   207  		parentNode = parent.Node()
   208  	)
   209  	parentNode.WithChildrenMu(func() {
   210  		childNode = parentNode.LookupChildLocked(name)
   211  		if childNode == nil {
   212  			// Common case. Performance hack which is used to allocate the node and
   213  			// its control FD together in the heap. For a well-behaving client, there
   214  			// will be a 1:1 mapping between control FD and node and their lifecycle
   215  			// will be similar too. This will help reduce allocations and memory
   216  			// fragmentation. This is more cache friendly too.
   217  			temp := struct {
   218  				node lisafs.Node
   219  				fd   controlFDLisa
   220  			}{}
   221  			childFD = &temp.fd
   222  			childNode = &temp.node
   223  			childNode.InitLocked(name, parentNode)
   224  		} else {
   225  			childNode.IncRef()
   226  			childFD = &controlFDLisa{}
   227  		}
   228  	})
   229  	childFD.hostFD = hostFD
   230  	childFD.writableHostFD = atomicbitops.FromInt32(-1)
   231  	childFD.ControlFD.Init(parent.Conn(), childNode, mode, childFD)
   232  	return childFD
   233  }
   234  
   235  func (fd *controlFDLisa) getWritableFD() (int, error) {
   236  	if writableFD := fd.writableHostFD.Load(); writableFD != -1 {
   237  		return int(writableFD), nil
   238  	}
   239  
   240  	writableFD, err := unix.Openat(int(procSelfFD.FD()), strconv.Itoa(fd.hostFD), (unix.O_WRONLY|openFlags)&^unix.O_NOFOLLOW, 0)
   241  	if err != nil {
   242  		return -1, err
   243  	}
   244  	if !fd.writableHostFD.CompareAndSwap(-1, int32(writableFD)) {
   245  		// Race detected, use the new value and clean this up.
   246  		unix.Close(writableFD)
   247  		return int(fd.writableHostFD.Load()), nil
   248  	}
   249  	return writableFD, nil
   250  }
   251  
   252  func (fd *controlFDLisa) getParentFD() (int, string, error) {
   253  	filePath := fd.Node().FilePath()
   254  	if filePath == "/" {
   255  		log.Warningf("getParentFD() call on the root")
   256  		return -1, "", unix.EINVAL
   257  	}
   258  	parent, err := unix.Open(path.Dir(filePath), openFlags|unix.O_PATH, 0)
   259  	return parent, path.Base(filePath), err
   260  }
   261  
   262  // FD implements lisafs.ControlFDImpl.FD.
   263  func (fd *controlFDLisa) FD() *lisafs.ControlFD {
   264  	if fd == nil {
   265  		return nil
   266  	}
   267  	return &fd.ControlFD
   268  }
   269  
   270  // Close implements lisafs.ControlFDImpl.Close.
   271  func (fd *controlFDLisa) Close() {
   272  	if fd.hostFD >= 0 {
   273  		_ = unix.Close(fd.hostFD)
   274  		fd.hostFD = -1
   275  	}
   276  	// No concurrent access is possible so no need to use atomics.
   277  	if fd.writableHostFD.RacyLoad() >= 0 {
   278  		_ = unix.Close(int(fd.writableHostFD.RacyLoad()))
   279  		fd.writableHostFD = atomicbitops.FromInt32(-1)
   280  	}
   281  }
   282  
   283  // Stat implements lisafs.ControlFDImpl.Stat.
   284  func (fd *controlFDLisa) Stat() (linux.Statx, error) {
   285  	return fstatTo(fd.hostFD)
   286  }
   287  
   288  // SetStat implements lisafs.ControlFDImpl.SetStat.
   289  func (fd *controlFDLisa) SetStat(stat lisafs.SetStatReq) (failureMask uint32, failureErr error) {
   290  	if stat.Mask&unix.STATX_MODE != 0 {
   291  		if fd.IsSocket() {
   292  			// fchmod(2) on socket files created via bind(2) fails. We need to
   293  			// fchmodat(2) it from its parent.
   294  			parent, sockName, err := fd.getParentFD()
   295  			if err == nil {
   296  				// Note that AT_SYMLINK_NOFOLLOW flag is not currently supported.
   297  				err = unix.Fchmodat(parent, sockName, stat.Mode&^unix.S_IFMT, 0 /* flags */)
   298  				unix.Close(parent)
   299  			}
   300  			if err != nil {
   301  				log.Warningf("SetStat fchmod failed on socket %q, err: %v", fd.Node().FilePath(), err)
   302  				failureMask |= unix.STATX_MODE
   303  				failureErr = err
   304  			}
   305  		} else {
   306  			if err := unix.Fchmod(fd.hostFD, stat.Mode&^unix.S_IFMT); err != nil {
   307  				log.Warningf("SetStat fchmod failed %q, err: %v", fd.Node().FilePath(), err)
   308  				failureMask |= unix.STATX_MODE
   309  				failureErr = err
   310  			}
   311  		}
   312  	}
   313  
   314  	if stat.Mask&unix.STATX_SIZE != 0 {
   315  		// ftruncate(2) requires the FD to be open for writing.
   316  		writableFD, err := fd.getWritableFD()
   317  		if err == nil {
   318  			err = unix.Ftruncate(writableFD, int64(stat.Size))
   319  		}
   320  		if err != nil {
   321  			log.Warningf("SetStat ftruncate failed %q, err: %v", fd.Node().FilePath(), err)
   322  			failureMask |= unix.STATX_SIZE
   323  			failureErr = err
   324  		}
   325  	}
   326  
   327  	if stat.Mask&(unix.STATX_ATIME|unix.STATX_MTIME) != 0 {
   328  		utimes := [2]unix.Timespec{
   329  			{Sec: 0, Nsec: unix.UTIME_OMIT},
   330  			{Sec: 0, Nsec: unix.UTIME_OMIT},
   331  		}
   332  		if stat.Mask&unix.STATX_ATIME != 0 {
   333  			utimes[0].Sec = stat.Atime.Sec
   334  			utimes[0].Nsec = stat.Atime.Nsec
   335  		}
   336  		if stat.Mask&unix.STATX_MTIME != 0 {
   337  			utimes[1].Sec = stat.Mtime.Sec
   338  			utimes[1].Nsec = stat.Mtime.Nsec
   339  		}
   340  
   341  		if fd.IsSymlink() {
   342  			// utimensat operates different that other syscalls. To operate on a
   343  			// symlink it *requires* AT_SYMLINK_NOFOLLOW with dirFD and a non-empty
   344  			// name. We need the parent FD.
   345  			parent, symlinkName, err := fd.getParentFD()
   346  			if err == nil {
   347  				err = fsutil.Utimensat(parent, symlinkName, utimes, unix.AT_SYMLINK_NOFOLLOW)
   348  				unix.Close(parent)
   349  			}
   350  			if err != nil {
   351  				failureMask |= (stat.Mask & (unix.STATX_ATIME | unix.STATX_MTIME))
   352  				failureErr = err
   353  			}
   354  		} else {
   355  			hostFD := fd.hostFD
   356  			if fd.IsRegular() {
   357  				// For regular files, utimensat(2) requires the FD to be open for
   358  				// writing, see BUGS section.
   359  				if writableFD, err := fd.getWritableFD(); err == nil {
   360  					hostFD = writableFD
   361  				} else {
   362  					log.Warningf("SetStat getWritableFD failed %q, err: %v", fd.Node().FilePath(), err)
   363  				}
   364  			}
   365  			// Directories and regular files can operate directly on the fd
   366  			// using empty name.
   367  			err := fsutil.Utimensat(hostFD, "", utimes, 0)
   368  			if err != nil {
   369  				log.Warningf("SetStat utimens failed %q, err: %v", fd.Node().FilePath(), err)
   370  				failureMask |= (stat.Mask & (unix.STATX_ATIME | unix.STATX_MTIME))
   371  				failureErr = err
   372  			}
   373  		}
   374  	}
   375  
   376  	if stat.Mask&(unix.STATX_UID|unix.STATX_GID) != 0 {
   377  		// "If the owner or group is specified as -1, then that ID is not changed"
   378  		// - chown(2)
   379  		uid := -1
   380  		if stat.Mask&unix.STATX_UID != 0 {
   381  			uid = int(stat.UID)
   382  		}
   383  		gid := -1
   384  		if stat.Mask&unix.STATX_GID != 0 {
   385  			gid = int(stat.GID)
   386  		}
   387  		if err := unix.Fchownat(fd.hostFD, "", uid, gid, unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW); err != nil {
   388  			log.Warningf("SetStat fchown failed %q, err: %v", fd.Node().FilePath(), err)
   389  			failureMask |= stat.Mask & (unix.STATX_UID | unix.STATX_GID)
   390  			failureErr = err
   391  		}
   392  	}
   393  
   394  	return
   395  }
   396  
   397  // Walk implements lisafs.ControlFDImpl.Walk.
   398  func (fd *controlFDLisa) Walk(name string) (*lisafs.ControlFD, linux.Statx, error) {
   399  	childHostFD, err := tryOpen(func(flags int) (int, error) {
   400  		return unix.Openat(fd.hostFD, name, flags, 0)
   401  	})
   402  	if err != nil {
   403  		return nil, linux.Statx{}, err
   404  	}
   405  
   406  	stat, err := fstatTo(childHostFD)
   407  	if err != nil {
   408  		_ = unix.Close(childHostFD)
   409  		return nil, linux.Statx{}, err
   410  	}
   411  
   412  	if err := checkSupportedFileType(uint32(stat.Mode)); err != nil {
   413  		_ = unix.Close(childHostFD)
   414  		log.Warningf("Walk: checkSupportedFileType() failed for %q with mode %o: %v", name, stat.Mode, err)
   415  		return nil, linux.Statx{}, err
   416  	}
   417  
   418  	return newControlFDLisa(childHostFD, fd, name, linux.FileMode(stat.Mode)).FD(), stat, nil
   419  }
   420  
   421  // WalkStat implements lisafs.ControlFDImpl.WalkStat.
   422  func (fd *controlFDLisa) WalkStat(path lisafs.StringArray, recordStat func(linux.Statx)) error {
   423  	// Note that while performing the walk below, we do not have read concurrency
   424  	// guarantee for any descendants. So files can be created/deleted inside fd
   425  	// while the walk is being performed. However, this should be fine from a
   426  	// security perspective as we are using host FDs to walk and checking that
   427  	// each opened path component is not a symlink.
   428  	curDirFD := fd.hostFD
   429  	closeCurDirFD := func() {
   430  		if curDirFD != fd.hostFD {
   431  			unix.Close(curDirFD)
   432  		}
   433  	}
   434  	defer closeCurDirFD()
   435  	if len(path) > 0 && len(path[0]) == 0 {
   436  		// Write stat results for dirFD if the first path component is "".
   437  		stat, err := fstatTo(fd.hostFD)
   438  		if err != nil {
   439  			return err
   440  		}
   441  		recordStat(stat)
   442  		path = path[1:]
   443  	}
   444  
   445  	// Don't attempt walking if parent is a symlink.
   446  	if fd.IsSymlink() {
   447  		return nil
   448  	}
   449  	for _, name := range path {
   450  		curFD, err := unix.Openat(curDirFD, name, unix.O_PATH|openFlags, 0)
   451  		if err == unix.ENOENT {
   452  			// No more path components exist on the filesystem. Return the partial
   453  			// walk to the client.
   454  			break
   455  		}
   456  		if err != nil {
   457  			return err
   458  		}
   459  		closeCurDirFD()
   460  		curDirFD = curFD
   461  
   462  		stat, err := fstatTo(curFD)
   463  		if err != nil {
   464  			return err
   465  		}
   466  		if err := checkSupportedFileType(uint32(stat.Mode)); err != nil {
   467  			log.Warningf("WalkStat: checkSupportedFileType() failed for file %q with mode %o while walking path %+v: %v", name, stat.Mode, path, err)
   468  			return err
   469  		}
   470  		recordStat(stat)
   471  
   472  		// Symlinks terminate walk. This client gets the symlink stat result, but
   473  		// will have to invoke Walk again with the resolved path.
   474  		if stat.Mode&unix.S_IFMT == unix.S_IFLNK {
   475  			break
   476  		}
   477  	}
   478  
   479  	return nil
   480  }
   481  
   482  // Used to log rejected fifo/uds operations, one time each.
   483  var (
   484  	logRejectedFifoOpenOnce   sync.Once
   485  	logRejectedUdsOpenOnce    sync.Once
   486  	logRejectedUdsCreateOnce  sync.Once
   487  	logRejectedUdsConnectOnce sync.Once
   488  )
   489  
   490  // Open implements lisafs.ControlFDImpl.Open.
   491  func (fd *controlFDLisa) Open(flags uint32) (*lisafs.OpenFD, int, error) {
   492  	ftype := fd.FileType()
   493  	server := fd.Conn().ServerImpl().(*LisafsServer)
   494  	switch ftype {
   495  	case unix.S_IFIFO:
   496  		if !server.config.HostFifo.AllowOpen() {
   497  			logRejectedFifoOpenOnce.Do(func() {
   498  				log.Warningf("Rejecting attempt to open fifo/pipe from host filesystem: %q. If you want to allow this, set flag --host-fifo=open", fd.ControlFD.Node().FilePath())
   499  			})
   500  			return nil, -1, unix.EPERM
   501  		}
   502  	case unix.S_IFSOCK:
   503  		if !server.config.HostUDS.AllowOpen() {
   504  			logRejectedUdsOpenOnce.Do(func() {
   505  				log.Warningf("Rejecting attempt to open unix domain socket from host filesystem. If you want to allow this, set flag --host-uds=open", fd.ControlFD.Node().FilePath())
   506  			})
   507  			return nil, -1, unix.EPERM
   508  		}
   509  	}
   510  	flags |= openFlags
   511  	openHostFD, err := unix.Openat(int(procSelfFD.FD()), strconv.Itoa(fd.hostFD), int(flags)&^unix.O_NOFOLLOW, 0)
   512  	if err != nil {
   513  		return nil, -1, err
   514  	}
   515  
   516  	hostFDToDonate := -1
   517  	switch {
   518  	case ftype == unix.S_IFREG:
   519  		// Best effort to donate file to the Sentry (for performance only).
   520  		hostFDToDonate, _ = unix.Dup(openHostFD)
   521  
   522  	case ftype == unix.S_IFIFO,
   523  		ftype == unix.S_IFCHR,
   524  		fd.isMountPoint && fd.Conn().ServerImpl().(*LisafsServer).config.DonateMountPointFD:
   525  		// Character devices and pipes can block indefinitely during reads/writes,
   526  		// which is not allowed for gofer operations. Ensure that it donates an FD
   527  		// back to the caller, so it can wait on the FD when reads/writes return
   528  		// EWOULDBLOCK. For mount points, if DonateMountPointFD option is set, an
   529  		// FD must be donated.
   530  		var err error
   531  		hostFDToDonate, err = unix.Dup(openHostFD)
   532  		if err != nil {
   533  			return nil, 0, err
   534  		}
   535  	}
   536  
   537  	openFD := fd.newOpenFDLisa(openHostFD, flags)
   538  	return openFD.FD(), hostFDToDonate, nil
   539  }
   540  
   541  // OpenCreate implements lisafs.ControlFDImpl.OpenCreate.
   542  func (fd *controlFDLisa) OpenCreate(mode linux.FileMode, uid lisafs.UID, gid lisafs.GID, name string, flags uint32) (*lisafs.ControlFD, linux.Statx, *lisafs.OpenFD, int, error) {
   543  	createFlags := unix.O_CREAT | unix.O_EXCL | unix.O_RDONLY | unix.O_NONBLOCK | openFlags
   544  	childHostFD, err := unix.Openat(fd.hostFD, name, createFlags, uint32(mode&^linux.FileTypeMask))
   545  	if err != nil {
   546  		return nil, linux.Statx{}, nil, -1, err
   547  	}
   548  
   549  	cu := cleanup.Make(func() {
   550  		// Best effort attempt to remove the file in case of failure.
   551  		if err := unix.Unlinkat(fd.hostFD, name, 0); err != nil {
   552  			log.Warningf("error unlinking file %q after failure: %v", path.Join(fd.Node().FilePath(), name), err)
   553  		}
   554  		unix.Close(childHostFD)
   555  	})
   556  	defer cu.Clean()
   557  
   558  	// Set the owners as requested by the client.
   559  	if err := unix.Fchownat(childHostFD, "", int(uid), int(gid), unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW); err != nil {
   560  		return nil, linux.Statx{}, nil, -1, err
   561  	}
   562  
   563  	// Get stat results.
   564  	childStat, err := fstatTo(childHostFD)
   565  	if err != nil {
   566  		return nil, linux.Statx{}, nil, -1, err
   567  	}
   568  
   569  	// Now open an FD to the newly created file with the flags requested by the client.
   570  	flags |= openFlags
   571  	newHostFD, err := unix.Openat(int(procSelfFD.FD()), strconv.Itoa(childHostFD), int(flags)&^unix.O_NOFOLLOW, 0)
   572  	if err != nil {
   573  		return nil, linux.Statx{}, nil, -1, err
   574  	}
   575  	cu.Release()
   576  
   577  	childFD := newControlFDLisa(childHostFD, fd, name, linux.ModeRegular)
   578  	newFD := childFD.newOpenFDLisa(newHostFD, uint32(flags))
   579  
   580  	// Donate FD because open(O_CREAT|O_EXCL) always creates a regular file.
   581  	// Since FD donation is a destructive operation, we should duplicate the
   582  	// to-be-donated FD. Eat the error if one occurs, it is better to have an FD
   583  	// without a host FD, than failing the Open attempt.
   584  	hostOpenFD := -1
   585  	if dupFD, err := unix.Dup(newFD.hostFD); err == nil {
   586  		hostOpenFD = dupFD
   587  	}
   588  
   589  	return childFD.FD(), childStat, newFD.FD(), hostOpenFD, nil
   590  }
   591  
   592  // Mkdir implements lisafs.ControlFDImpl.Mkdir.
   593  func (fd *controlFDLisa) Mkdir(mode linux.FileMode, uid lisafs.UID, gid lisafs.GID, name string) (*lisafs.ControlFD, linux.Statx, error) {
   594  	if err := unix.Mkdirat(fd.hostFD, name, uint32(mode&^linux.FileTypeMask)); err != nil {
   595  		return nil, linux.Statx{}, err
   596  	}
   597  	cu := cleanup.Make(func() {
   598  		// Best effort attempt to remove the dir in case of failure.
   599  		if err := unix.Unlinkat(fd.hostFD, name, unix.AT_REMOVEDIR); err != nil {
   600  			log.Warningf("error unlinking dir %q after failure: %v", path.Join(fd.Node().FilePath(), name), err)
   601  		}
   602  	})
   603  	defer cu.Clean()
   604  
   605  	// Open directory to change ownership.
   606  	childDirFd, err := tryOpen(func(flags int) (int, error) {
   607  		return unix.Openat(fd.hostFD, name, flags|unix.O_DIRECTORY, 0)
   608  	})
   609  	if err != nil {
   610  		return nil, linux.Statx{}, err
   611  	}
   612  	if err := unix.Fchownat(childDirFd, "", int(uid), int(gid), unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW); err != nil {
   613  		unix.Close(childDirFd)
   614  		return nil, linux.Statx{}, err
   615  	}
   616  
   617  	// Get stat results.
   618  	childDirStat, err := fstatTo(childDirFd)
   619  	if err != nil {
   620  		unix.Close(childDirFd)
   621  		return nil, linux.Statx{}, err
   622  	}
   623  
   624  	cu.Release()
   625  	return newControlFDLisa(childDirFd, fd, name, linux.ModeDirectory).FD(), childDirStat, nil
   626  }
   627  
   628  // Mknod implements lisafs.ControlFDImpl.Mknod.
   629  func (fd *controlFDLisa) Mknod(mode linux.FileMode, uid lisafs.UID, gid lisafs.GID, name string, minor uint32, major uint32) (*lisafs.ControlFD, linux.Statx, error) {
   630  	// From mknod(2) man page:
   631  	// "EPERM: [...] if the filesystem containing pathname does not support
   632  	// the type of node requested."
   633  	if mode.FileType() != linux.ModeRegular {
   634  		return nil, linux.Statx{}, unix.EPERM
   635  	}
   636  
   637  	if err := unix.Mknodat(fd.hostFD, name, uint32(mode), 0); err != nil {
   638  		return nil, linux.Statx{}, err
   639  	}
   640  	cu := cleanup.Make(func() {
   641  		// Best effort attempt to remove the file in case of failure.
   642  		if err := unix.Unlinkat(fd.hostFD, name, 0); err != nil {
   643  			log.Warningf("error unlinking file %q after failure: %v", path.Join(fd.Node().FilePath(), name), err)
   644  		}
   645  	})
   646  	defer cu.Clean()
   647  
   648  	// Open file to change ownership.
   649  	childFD, err := tryOpen(func(flags int) (int, error) {
   650  		return unix.Openat(fd.hostFD, name, flags, 0)
   651  	})
   652  	if err != nil {
   653  		return nil, linux.Statx{}, err
   654  	}
   655  	if err := unix.Fchownat(childFD, "", int(uid), int(gid), unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW); err != nil {
   656  		unix.Close(childFD)
   657  		return nil, linux.Statx{}, err
   658  	}
   659  
   660  	// Get stat results.
   661  	childStat, err := fstatTo(childFD)
   662  	if err != nil {
   663  		unix.Close(childFD)
   664  		return nil, linux.Statx{}, err
   665  	}
   666  	cu.Release()
   667  
   668  	return newControlFDLisa(childFD, fd, name, mode).FD(), childStat, nil
   669  }
   670  
   671  // Symlink implements lisafs.ControlFDImpl.Symlink.
   672  func (fd *controlFDLisa) Symlink(name string, target string, uid lisafs.UID, gid lisafs.GID) (*lisafs.ControlFD, linux.Statx, error) {
   673  	if err := unix.Symlinkat(target, fd.hostFD, name); err != nil {
   674  		return nil, linux.Statx{}, err
   675  	}
   676  	cu := cleanup.Make(func() {
   677  		// Best effort attempt to remove the symlink in case of failure.
   678  		if err := unix.Unlinkat(fd.hostFD, name, 0); err != nil {
   679  			log.Warningf("error unlinking file %q after failure: %v", path.Join(fd.Node().FilePath(), name), err)
   680  		}
   681  	})
   682  	defer cu.Clean()
   683  
   684  	// Open symlink to change ownership.
   685  	symlinkFD, err := unix.Openat(fd.hostFD, name, unix.O_PATH|openFlags, 0)
   686  	if err != nil {
   687  		return nil, linux.Statx{}, err
   688  	}
   689  	if err := unix.Fchownat(symlinkFD, "", int(uid), int(gid), unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW); err != nil {
   690  		unix.Close(symlinkFD)
   691  		return nil, linux.Statx{}, err
   692  	}
   693  
   694  	symlinkStat, err := fstatTo(symlinkFD)
   695  	if err != nil {
   696  		unix.Close(symlinkFD)
   697  		return nil, linux.Statx{}, err
   698  	}
   699  	cu.Release()
   700  	return newControlFDLisa(symlinkFD, fd, name, linux.ModeSymlink).FD(), symlinkStat, nil
   701  }
   702  
   703  // Link implements lisafs.ControlFDImpl.Link.
   704  func (fd *controlFDLisa) Link(dir lisafs.ControlFDImpl, name string) (*lisafs.ControlFD, linux.Statx, error) {
   705  	// Using linkat(targetFD, "", newdirfd, name, AT_EMPTY_PATH) requires
   706  	// CAP_DAC_READ_SEARCH in the *root* userns. The gofer process has
   707  	// CAP_DAC_READ_SEARCH in its own userns. But sometimes the gofer may be
   708  	// running in a different userns. So we can't use AT_EMPTY_PATH. Fallback
   709  	// to using olddirfd to call linkat(2).
   710  	oldDirFD, oldName, err := fd.getParentFD()
   711  	if err != nil {
   712  		return nil, linux.Statx{}, err
   713  	}
   714  	dirFD := dir.(*controlFDLisa)
   715  	if err := unix.Linkat(oldDirFD, oldName, dirFD.hostFD, name, 0); err != nil {
   716  		return nil, linux.Statx{}, err
   717  	}
   718  	cu := cleanup.Make(func() {
   719  		// Best effort attempt to remove the hard link in case of failure.
   720  		if err := unix.Unlinkat(dirFD.hostFD, name, 0); err != nil {
   721  			log.Warningf("error unlinking file %q after failure: %v", path.Join(dirFD.Node().FilePath(), name), err)
   722  		}
   723  	})
   724  	defer cu.Clean()
   725  
   726  	linkFD, err := tryOpen(func(flags int) (int, error) {
   727  		return unix.Openat(dirFD.hostFD, name, flags, 0)
   728  	})
   729  	if err != nil {
   730  		return nil, linux.Statx{}, err
   731  	}
   732  
   733  	linkStat, err := fstatTo(linkFD)
   734  	if err != nil {
   735  		return nil, linux.Statx{}, err
   736  	}
   737  	cu.Release()
   738  	return newControlFDLisa(linkFD, dirFD, name, linux.FileMode(linkStat.Mode)).FD(), linkStat, nil
   739  }
   740  
   741  // StatFS implements lisafs.ControlFDImpl.StatFS.
   742  func (fd *controlFDLisa) StatFS() (lisafs.StatFS, error) {
   743  	var s unix.Statfs_t
   744  	if err := unix.Fstatfs(fd.hostFD, &s); err != nil {
   745  		return lisafs.StatFS{}, err
   746  	}
   747  
   748  	return lisafs.StatFS{
   749  		Type:            uint64(s.Type),
   750  		BlockSize:       s.Bsize,
   751  		Blocks:          s.Blocks,
   752  		BlocksFree:      s.Bfree,
   753  		BlocksAvailable: s.Bavail,
   754  		Files:           s.Files,
   755  		FilesFree:       s.Ffree,
   756  		NameLength:      uint64(s.Namelen),
   757  	}, nil
   758  }
   759  
   760  // Readlink implements lisafs.ControlFDImpl.Readlink.
   761  func (fd *controlFDLisa) Readlink(getLinkBuf func(uint32) []byte) (uint16, error) {
   762  	// This is similar to what os.Readlink does.
   763  	for linkLen := 128; linkLen < math.MaxUint16; linkLen *= 2 {
   764  		b := getLinkBuf(uint32(linkLen))
   765  		n, err := unix.Readlinkat(fd.hostFD, "", b)
   766  		if err != nil {
   767  			return 0, err
   768  		}
   769  		if n < int(linkLen) {
   770  			return uint16(n), nil
   771  		}
   772  	}
   773  	return 0, unix.ENOMEM
   774  }
   775  
   776  func isSockTypeSupported(sockType uint32) bool {
   777  	switch sockType {
   778  	case unix.SOCK_STREAM, unix.SOCK_DGRAM, unix.SOCK_SEQPACKET:
   779  		return true
   780  	default:
   781  		log.Debugf("socket type %d is not supported", sockType)
   782  		return false
   783  	}
   784  }
   785  
   786  // Connect implements lisafs.ControlFDImpl.Connect.
   787  func (fd *controlFDLisa) Connect(sockType uint32) (int, error) {
   788  	if !fd.Conn().ServerImpl().(*LisafsServer).config.HostUDS.AllowOpen() {
   789  		logRejectedUdsConnectOnce.Do(func() {
   790  			log.Warningf("Rejecting attempt to connect to unix domain socket from host filesystem: %q. If you want to allow this, set flag --host-uds=open", fd.ControlFD.Node().FilePath())
   791  		})
   792  		return -1, unix.EPERM
   793  	}
   794  
   795  	// TODO(gvisor.dev/issue/1003): Due to different app vs replacement
   796  	// mappings, the app path may have fit in the sockaddr, but we can't fit
   797  	// hostPath in our sockaddr. We'd need to redirect through a shorter path
   798  	// in order to actually connect to this socket.
   799  	hostPath := fd.Node().FilePath()
   800  	if len(hostPath) >= linux.UnixPathMax {
   801  		return -1, unix.EINVAL
   802  	}
   803  
   804  	if !isSockTypeSupported(sockType) {
   805  		return -1, unix.ENXIO
   806  	}
   807  
   808  	sock, err := unix.Socket(unix.AF_UNIX, int(sockType), 0)
   809  	if err != nil {
   810  		return -1, err
   811  	}
   812  
   813  	sa := unix.SockaddrUnix{Name: hostPath}
   814  	if err := unix.Connect(sock, &sa); err != nil {
   815  		unix.Close(sock)
   816  		return -1, err
   817  	}
   818  	return sock, nil
   819  }
   820  
   821  // BindAt implements lisafs.ControlFDImpl.BindAt.
   822  func (fd *controlFDLisa) BindAt(name string, sockType uint32, mode linux.FileMode, uid lisafs.UID, gid lisafs.GID) (*lisafs.ControlFD, linux.Statx, *lisafs.BoundSocketFD, int, error) {
   823  	if !fd.Conn().ServerImpl().(*LisafsServer).config.HostUDS.AllowCreate() {
   824  		logRejectedUdsCreateOnce.Do(func() {
   825  			log.Warningf("Rejecting attempt to create unix domain socket from host filesystem: %q. If you want to allow this, set flag --host-uds=create", name)
   826  		})
   827  		return nil, linux.Statx{}, nil, -1, unix.EPERM
   828  	}
   829  
   830  	// Because there is no "bindat" syscall in Linux, we must create an
   831  	// absolute path to the socket we are creating,
   832  	socketPath := filepath.Join(fd.Node().FilePath(), name)
   833  
   834  	// TODO(gvisor.dev/issue/1003): Due to different app vs replacement
   835  	// mappings, the app path may have fit in the sockaddr, but we can't fit
   836  	// hostPath in our sockaddr. We'd need to redirect through a shorter path
   837  	// in order to actually connect to this socket.
   838  	if len(socketPath) >= linux.UnixPathMax {
   839  		log.Warningf("BindAt called with name too long: %q (len=%d)", socketPath, len(socketPath))
   840  		return nil, linux.Statx{}, nil, -1, unix.EINVAL
   841  	}
   842  
   843  	// Only the following types are supported.
   844  	if !isSockTypeSupported(sockType) {
   845  		return nil, linux.Statx{}, nil, -1, unix.ENXIO
   846  	}
   847  
   848  	// Create and bind the socket using the sockPath which may be a
   849  	// symlink.
   850  	sockFD, err := unix.Socket(unix.AF_UNIX, int(sockType), 0)
   851  	if err != nil {
   852  		return nil, linux.Statx{}, nil, -1, err
   853  	}
   854  	cu := cleanup.Make(func() {
   855  		_ = unix.Close(sockFD)
   856  	})
   857  	defer cu.Clean()
   858  
   859  	// fchmod(2) has to happen *before* the bind(2). sockFD's file mode will
   860  	// be used in creating the filesystem-object in bind(2).
   861  	if err := unix.Fchmod(sockFD, uint32(mode&^linux.FileTypeMask)); err != nil {
   862  		return nil, linux.Statx{}, nil, -1, err
   863  	}
   864  
   865  	if err := unix.Bind(sockFD, &unix.SockaddrUnix{Name: socketPath}); err != nil {
   866  		return nil, linux.Statx{}, nil, -1, err
   867  	}
   868  	cu.Add(func() {
   869  		_ = unix.Unlink(socketPath)
   870  	})
   871  
   872  	sockFileFD, err := tryOpen(func(flags int) (int, error) {
   873  		return unix.Openat(fd.hostFD, name, flags, 0)
   874  	})
   875  	if err != nil {
   876  		return nil, linux.Statx{}, nil, -1, err
   877  	}
   878  	cu.Add(func() {
   879  		_ = unix.Close(sockFileFD)
   880  	})
   881  
   882  	if err := unix.Fchownat(sockFileFD, "", int(uid), int(gid), unix.AT_EMPTY_PATH|unix.AT_SYMLINK_NOFOLLOW); err != nil {
   883  		return nil, linux.Statx{}, nil, -1, err
   884  	}
   885  
   886  	// Stat the socket.
   887  	sockStat, err := fstatTo(sockFileFD)
   888  	if err != nil {
   889  		return nil, linux.Statx{}, nil, -1, err
   890  	}
   891  
   892  	// Create an FD that will be donated to the sandbox.
   893  	sockFDToDonate, err := unix.Dup(sockFD)
   894  	if err != nil {
   895  		return nil, linux.Statx{}, nil, -1, err
   896  	}
   897  	cu.Release()
   898  
   899  	socketControlFD := newControlFDLisa(sockFD, fd, name, linux.ModeSocket)
   900  	boundSocketFD := &boundSocketFDLisa{
   901  		sock: os.NewFile(uintptr(sockFD), socketPath),
   902  	}
   903  	boundSocketFD.Init(socketControlFD.FD(), boundSocketFD)
   904  
   905  	return socketControlFD.FD(), sockStat, boundSocketFD.FD(), sockFDToDonate, nil
   906  }
   907  
   908  // Unlink implements lisafs.ControlFDImpl.Unlink.
   909  func (fd *controlFDLisa) Unlink(name string, flags uint32) error {
   910  	return unix.Unlinkat(fd.hostFD, name, int(flags))
   911  }
   912  
   913  // RenameAt implements lisafs.ControlFDImpl.RenameAt.
   914  func (fd *controlFDLisa) RenameAt(oldName string, newDir lisafs.ControlFDImpl, newName string) error {
   915  	return fsutil.RenameAt(fd.hostFD, oldName, newDir.(*controlFDLisa).hostFD, newName)
   916  }
   917  
   918  // Renamed implements lisafs.ControlFDImpl.Renamed.
   919  func (fd *controlFDLisa) Renamed() {
   920  	// controlFDLisa does not have any state to update on rename.
   921  }
   922  
   923  // GetXattr implements lisafs.ControlFDImpl.GetXattr.
   924  func (fd *controlFDLisa) GetXattr(name string, size uint32, getValueBuf func(uint32) []byte) (uint16, error) {
   925  	data := getValueBuf(size)
   926  	xattrSize, err := unix.Fgetxattr(fd.hostFD, name, data)
   927  	return uint16(xattrSize), err
   928  }
   929  
   930  // SetXattr implements lisafs.ControlFDImpl.SetXattr.
   931  func (fd *controlFDLisa) SetXattr(name string, value string, flags uint32) error {
   932  	return unix.EOPNOTSUPP
   933  }
   934  
   935  // ListXattr implements lisafs.ControlFDImpl.ListXattr.
   936  func (fd *controlFDLisa) ListXattr(size uint64) (lisafs.StringArray, error) {
   937  	return nil, unix.EOPNOTSUPP
   938  }
   939  
   940  // RemoveXattr implements lisafs.ControlFDImpl.RemoveXattr.
   941  func (fd *controlFDLisa) RemoveXattr(name string) error {
   942  	return unix.EOPNOTSUPP
   943  }
   944  
   945  // openFDLisa implements lisafs.OpenFDImpl.
   946  type openFDLisa struct {
   947  	lisafs.OpenFD
   948  
   949  	// hostFD is the host file descriptor which can be used to make syscalls.
   950  	hostFD int
   951  }
   952  
   953  var _ lisafs.OpenFDImpl = (*openFDLisa)(nil)
   954  
   955  func (fd *controlFDLisa) newOpenFDLisa(hostFD int, flags uint32) *openFDLisa {
   956  	newFD := &openFDLisa{
   957  		hostFD: hostFD,
   958  	}
   959  	newFD.OpenFD.Init(fd.FD(), flags, newFD)
   960  	return newFD
   961  }
   962  
   963  // FD implements lisafs.OpenFDImpl.FD.
   964  func (fd *openFDLisa) FD() *lisafs.OpenFD {
   965  	if fd == nil {
   966  		return nil
   967  	}
   968  	return &fd.OpenFD
   969  }
   970  
   971  // Close implements lisafs.OpenFDImpl.Close.
   972  func (fd *openFDLisa) Close() {
   973  	if fd.hostFD >= 0 {
   974  		_ = unix.Close(fd.hostFD)
   975  		fd.hostFD = -1
   976  	}
   977  }
   978  
   979  // Stat implements lisafs.OpenFDImpl.Stat.
   980  func (fd *openFDLisa) Stat() (linux.Statx, error) {
   981  	return fstatTo(fd.hostFD)
   982  }
   983  
   984  // Sync implements lisafs.OpenFDImpl.Sync.
   985  func (fd *openFDLisa) Sync() error {
   986  	return unix.Fsync(fd.hostFD)
   987  }
   988  
   989  // Write implements lisafs.OpenFDImpl.Write.
   990  func (fd *openFDLisa) Write(buf []byte, off uint64) (uint64, error) {
   991  	rw := rwfd.NewReadWriter(fd.hostFD)
   992  	n, err := rw.WriteAt(buf, int64(off))
   993  	return uint64(n), err
   994  }
   995  
   996  // Read implements lisafs.OpenFDImpl.Read.
   997  func (fd *openFDLisa) Read(buf []byte, off uint64) (uint64, error) {
   998  	rw := rwfd.NewReadWriter(fd.hostFD)
   999  	n, err := rw.ReadAt(buf, int64(off))
  1000  	if err != nil && err != io.EOF {
  1001  		return 0, err
  1002  	}
  1003  	return uint64(n), nil
  1004  }
  1005  
  1006  // Allocate implements lisafs.OpenFDImpl.Allocate.
  1007  func (fd *openFDLisa) Allocate(mode, off, length uint64) error {
  1008  	return unix.Fallocate(fd.hostFD, uint32(mode), int64(off), int64(length))
  1009  }
  1010  
  1011  // Flush implements lisafs.OpenFDImpl.Flush.
  1012  func (fd *openFDLisa) Flush() error {
  1013  	return nil
  1014  }
  1015  
  1016  // Getdent64 implements lisafs.OpenFDImpl.Getdent64.
  1017  func (fd *openFDLisa) Getdent64(count uint32, seek0 bool, recordDirent func(lisafs.Dirent64)) error {
  1018  	if seek0 {
  1019  		if _, err := unix.Seek(fd.hostFD, 0, 0); err != nil {
  1020  			return err
  1021  		}
  1022  	}
  1023  
  1024  	var direntsBuf [8192]byte
  1025  	var bytesRead int
  1026  	for bytesRead < int(count) {
  1027  		bufEnd := len(direntsBuf)
  1028  		if remaining := int(count) - bytesRead; remaining < bufEnd {
  1029  			bufEnd = remaining
  1030  		}
  1031  		n, err := unix.Getdents(fd.hostFD, direntsBuf[:bufEnd])
  1032  		if err != nil {
  1033  			if err == unix.EINVAL && bufEnd < fsutil.UnixDirentMaxSize {
  1034  				// getdents64(2) returns EINVAL is returned when the result
  1035  				// buffer is too small. If bufEnd is smaller than the max
  1036  				// size of unix.Dirent, then just break here to return all
  1037  				// dirents collected till now.
  1038  				break
  1039  			}
  1040  			return err
  1041  		}
  1042  		if n <= 0 {
  1043  			break
  1044  		}
  1045  
  1046  		fsutil.ParseDirents(direntsBuf[:n], func(ino uint64, off int64, ftype uint8, name string, reclen uint16) {
  1047  			dirent := lisafs.Dirent64{
  1048  				Ino:  primitive.Uint64(ino),
  1049  				Off:  primitive.Uint64(off),
  1050  				Type: primitive.Uint8(ftype),
  1051  				Name: lisafs.SizedString(name),
  1052  			}
  1053  
  1054  			// The client also wants the device ID, which annoyingly incurs an
  1055  			// additional syscall per dirent.
  1056  			// TODO(gvisor.dev/issue/6665): Get rid of per-dirent stat.
  1057  			stat, err := fsutil.StatAt(fd.hostFD, name)
  1058  			if err != nil {
  1059  				log.Warningf("Getdent64: skipping file %q with failed stat, err: %v", path.Join(fd.ControlFD().FD().Node().FilePath(), name), err)
  1060  				return
  1061  			}
  1062  			dirent.DevMinor = primitive.Uint32(unix.Minor(stat.Dev))
  1063  			dirent.DevMajor = primitive.Uint32(unix.Major(stat.Dev))
  1064  			recordDirent(dirent)
  1065  			bytesRead += int(reclen)
  1066  		})
  1067  	}
  1068  	return nil
  1069  }
  1070  
  1071  // Renamed implements lisafs.OpenFDImpl.Renamed.
  1072  func (fd *openFDLisa) Renamed() {
  1073  	// openFDLisa does not have any state to update on rename.
  1074  }
  1075  
  1076  type boundSocketFDLisa struct {
  1077  	lisafs.BoundSocketFD
  1078  
  1079  	sock *os.File
  1080  }
  1081  
  1082  var _ lisafs.BoundSocketFDImpl = (*boundSocketFDLisa)(nil)
  1083  
  1084  // Close implements lisafs.BoundSocketFD.Close.
  1085  func (fd *boundSocketFDLisa) Close() {
  1086  	fd.sock.Close()
  1087  }
  1088  
  1089  // FD implements lisafs.BoundSocketFD.FD.
  1090  func (fd *boundSocketFDLisa) FD() *lisafs.BoundSocketFD {
  1091  	if fd == nil {
  1092  		return nil
  1093  	}
  1094  	return &fd.BoundSocketFD
  1095  }
  1096  
  1097  // Listen implements lisafs.BoundSocketFD.Listen.
  1098  func (fd *boundSocketFDLisa) Listen(backlog int32) error {
  1099  	return unix.Listen(int(fd.sock.Fd()), int(backlog))
  1100  }
  1101  
  1102  // Listen implements lisafs.BoundSocketFD.Accept.
  1103  func (fd *boundSocketFDLisa) Accept() (int, string, error) {
  1104  	flags := unix.O_NONBLOCK | unix.O_CLOEXEC
  1105  	nfd, _, err := unix.Accept4(int(fd.sock.Fd()), flags)
  1106  	if err != nil {
  1107  		return -1, "", err
  1108  	}
  1109  	// Return an empty peer address so that we don't leak the actual host
  1110  	// address.
  1111  	return nfd, "", err
  1112  }
  1113  
  1114  // tryOpen tries to open() with different modes as documented.
  1115  func tryOpen(open func(int) (int, error)) (hostFD int, err error) {
  1116  	// Attempt to open file in the following in order:
  1117  	//   1. RDONLY | NONBLOCK: for all files, directories, ro mounts, FIFOs.
  1118  	//      Use non-blocking to prevent getting stuck inside open(2) for
  1119  	//      FIFOs. This option has no effect on regular files.
  1120  	//   2. PATH: for symlinks, sockets.
  1121  	flags := []int{
  1122  		unix.O_RDONLY | unix.O_NONBLOCK,
  1123  		unix.O_PATH,
  1124  	}
  1125  
  1126  	for _, flag := range flags {
  1127  		hostFD, err = open(flag | openFlags)
  1128  		if err == nil {
  1129  			return
  1130  		}
  1131  
  1132  		if e := extractErrno(err); e == unix.ENOENT {
  1133  			// File doesn't exist, no point in retrying.
  1134  			return -1, e
  1135  		}
  1136  	}
  1137  	return
  1138  }
  1139  
  1140  func fstatTo(hostFD int) (linux.Statx, error) {
  1141  	var stat unix.Stat_t
  1142  	if err := unix.Fstat(hostFD, &stat); err != nil {
  1143  		return linux.Statx{}, err
  1144  	}
  1145  
  1146  	return linux.Statx{
  1147  		Mask:      unix.STATX_TYPE | unix.STATX_MODE | unix.STATX_INO | unix.STATX_NLINK | unix.STATX_UID | unix.STATX_GID | unix.STATX_SIZE | unix.STATX_BLOCKS | unix.STATX_ATIME | unix.STATX_MTIME | unix.STATX_CTIME,
  1148  		Mode:      uint16(stat.Mode),
  1149  		DevMinor:  unix.Minor(stat.Dev),
  1150  		DevMajor:  unix.Major(stat.Dev),
  1151  		Ino:       stat.Ino,
  1152  		Nlink:     uint32(stat.Nlink),
  1153  		UID:       stat.Uid,
  1154  		GID:       stat.Gid,
  1155  		RdevMinor: unix.Minor(stat.Rdev),
  1156  		RdevMajor: unix.Major(stat.Rdev),
  1157  		Size:      uint64(stat.Size),
  1158  		Blksize:   uint32(stat.Blksize),
  1159  		Blocks:    uint64(stat.Blocks),
  1160  		Atime: linux.StatxTimestamp{
  1161  			Sec:  stat.Atim.Sec,
  1162  			Nsec: uint32(stat.Atim.Nsec),
  1163  		},
  1164  		Mtime: linux.StatxTimestamp{
  1165  			Sec:  stat.Mtim.Sec,
  1166  			Nsec: uint32(stat.Mtim.Nsec),
  1167  		},
  1168  		Ctime: linux.StatxTimestamp{
  1169  			Sec:  stat.Ctim.Sec,
  1170  			Nsec: uint32(stat.Ctim.Nsec),
  1171  		},
  1172  	}, nil
  1173  }
  1174  
  1175  func checkSupportedFileType(mode uint32) error {
  1176  	switch mode & unix.S_IFMT {
  1177  	case unix.S_IFREG, unix.S_IFDIR, unix.S_IFLNK, unix.S_IFCHR, unix.S_IFSOCK, unix.S_IFIFO:
  1178  		return nil
  1179  
  1180  	default:
  1181  		return unix.EPERM
  1182  	}
  1183  }
  1184  
  1185  // extractErrno tries to determine the errno.
  1186  func extractErrno(err error) unix.Errno {
  1187  	if err == nil {
  1188  		// This should never happen. The likely result will be that
  1189  		// some user gets the frustrating "error: SUCCESS" message.
  1190  		log.Warningf("extractErrno called with nil error!")
  1191  		return 0
  1192  	}
  1193  
  1194  	switch err {
  1195  	case os.ErrNotExist:
  1196  		return unix.ENOENT
  1197  	case os.ErrExist:
  1198  		return unix.EEXIST
  1199  	case os.ErrPermission:
  1200  		return unix.EACCES
  1201  	case os.ErrInvalid:
  1202  		return unix.EINVAL
  1203  	}
  1204  
  1205  	// See if it's an errno or a common wrapped error.
  1206  	switch e := err.(type) {
  1207  	case unix.Errno:
  1208  		return e
  1209  	case *os.PathError:
  1210  		return extractErrno(e.Err)
  1211  	case *os.LinkError:
  1212  		return extractErrno(e.Err)
  1213  	case *os.SyscallError:
  1214  		return extractErrno(e.Err)
  1215  	}
  1216  
  1217  	// Fall back to EIO.
  1218  	log.Debugf("Unknown error: %v, defaulting to EIO", err)
  1219  	return unix.EIO
  1220  }
  1221  
  1222  // LINT.ThenChange(../../pkg/sentry/fsimpl/gofer/directfs_dentry.go)