github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/p9/client_file.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 p9
    16  
    17  import (
    18  	"errors"
    19  	"fmt"
    20  	"io"
    21  
    22  	"github.com/ttpreport/gvisor-ligolo/pkg/atomicbitops"
    23  	"github.com/ttpreport/gvisor-ligolo/pkg/fd"
    24  	"github.com/ttpreport/gvisor-ligolo/pkg/log"
    25  	"golang.org/x/sys/unix"
    26  )
    27  
    28  // Attach attaches to a server.
    29  //
    30  // Note that authentication is not currently supported.
    31  func (c *Client) Attach(name string) (File, error) {
    32  	fid, ok := c.fidPool.Get()
    33  	if !ok {
    34  		return nil, ErrOutOfFIDs
    35  	}
    36  
    37  	rattach := Rattach{}
    38  	if err := c.sendRecv(&Tattach{FID: FID(fid), Auth: Tauth{AttachName: name, AuthenticationFID: NoFID, UID: NoUID}}, &rattach); err != nil {
    39  		c.fidPool.Put(fid)
    40  		return nil, err
    41  	}
    42  
    43  	return c.newFile(FID(fid)), nil
    44  }
    45  
    46  // newFile returns a new client file.
    47  func (c *Client) newFile(fid FID) *clientFile {
    48  	return &clientFile{
    49  		client: c,
    50  		fid:    fid,
    51  	}
    52  }
    53  
    54  // clientFile is provided to clients.
    55  //
    56  // This proxies all of the interfaces found in file.go.
    57  type clientFile struct {
    58  	DisallowServerCalls
    59  
    60  	// client is the originating client.
    61  	client *Client
    62  
    63  	// fid is the FID for this file.
    64  	fid FID
    65  
    66  	// closed indicates whether this file has been closed.
    67  	closed atomicbitops.Uint32
    68  }
    69  
    70  // Walk implements File.Walk.
    71  func (c *clientFile) Walk(names []string) ([]QID, File, error) {
    72  	if c.closed.Load() != 0 {
    73  		return nil, nil, unix.EBADF
    74  	}
    75  
    76  	fid, ok := c.client.fidPool.Get()
    77  	if !ok {
    78  		return nil, nil, ErrOutOfFIDs
    79  	}
    80  
    81  	rwalk := Rwalk{}
    82  	if err := c.client.sendRecv(&Twalk{FID: c.fid, NewFID: FID(fid), Names: names}, &rwalk); err != nil {
    83  		c.client.fidPool.Put(fid)
    84  		return nil, nil, err
    85  	}
    86  
    87  	// Return a new client file.
    88  	return rwalk.QIDs, c.client.newFile(FID(fid)), nil
    89  }
    90  
    91  // WalkGetAttr implements File.WalkGetAttr.
    92  func (c *clientFile) WalkGetAttr(components []string) ([]QID, File, AttrMask, Attr, error) {
    93  	if c.closed.Load() != 0 {
    94  		return nil, nil, AttrMask{}, Attr{}, unix.EBADF
    95  	}
    96  
    97  	if !versionSupportsTwalkgetattr(c.client.version) {
    98  		qids, file, err := c.Walk(components)
    99  		if err != nil {
   100  			return nil, nil, AttrMask{}, Attr{}, err
   101  		}
   102  		_, valid, attr, err := file.GetAttr(AttrMaskAll())
   103  		if err != nil {
   104  			file.Close()
   105  			return nil, nil, AttrMask{}, Attr{}, err
   106  		}
   107  		return qids, file, valid, attr, nil
   108  	}
   109  
   110  	fid, ok := c.client.fidPool.Get()
   111  	if !ok {
   112  		return nil, nil, AttrMask{}, Attr{}, ErrOutOfFIDs
   113  	}
   114  
   115  	rwalkgetattr := Rwalkgetattr{}
   116  	if err := c.client.sendRecv(&Twalkgetattr{FID: c.fid, NewFID: FID(fid), Names: components}, &rwalkgetattr); err != nil {
   117  		c.client.fidPool.Put(fid)
   118  		return nil, nil, AttrMask{}, Attr{}, err
   119  	}
   120  
   121  	// Return a new client file.
   122  	return rwalkgetattr.QIDs, c.client.newFile(FID(fid)), rwalkgetattr.Valid, rwalkgetattr.Attr, nil
   123  }
   124  
   125  func (c *clientFile) MultiGetAttr(names []string) ([]FullStat, error) {
   126  	if c.closed.Load() != 0 {
   127  		return nil, unix.EBADF
   128  	}
   129  
   130  	if versionSupportsTmultiGetAttr(c.client.version) {
   131  		rmultigetattr := Rmultigetattr{}
   132  		if err := c.client.sendRecv(&Tmultigetattr{FID: c.fid, Names: names}, &rmultigetattr); err != nil {
   133  			return nil, err
   134  		}
   135  		return rmultigetattr.Stats, nil
   136  	}
   137  
   138  	stats := make([]FullStat, 0, len(names))
   139  	var start File = c
   140  	parent := start
   141  	closeParent := func() {
   142  		if parent != start {
   143  			_ = parent.Close()
   144  		}
   145  	}
   146  	defer closeParent()
   147  	mask := AttrMaskAll()
   148  	for i, name := range names {
   149  		if len(name) == 0 && i == 0 {
   150  			qid, valid, attr, err := parent.GetAttr(mask)
   151  			if err != nil {
   152  				return nil, err
   153  			}
   154  			stats = append(stats, FullStat{
   155  				QID:   qid,
   156  				Valid: valid,
   157  				Attr:  attr,
   158  			})
   159  			continue
   160  		}
   161  		qids, child, valid, attr, err := parent.WalkGetAttr([]string{name})
   162  		if err != nil {
   163  			if errors.Is(err, unix.ENOENT) {
   164  				return stats, nil
   165  			}
   166  			return nil, err
   167  		}
   168  		closeParent()
   169  		parent = child
   170  		stats = append(stats, FullStat{
   171  			QID:   qids[0],
   172  			Valid: valid,
   173  			Attr:  attr,
   174  		})
   175  		if attr.Mode.FileType() != ModeDirectory {
   176  			// Doesn't need to continue if entry is not a dir. Including symlinks
   177  			// that cannot be followed.
   178  			break
   179  		}
   180  	}
   181  	return stats, nil
   182  }
   183  
   184  // StatFS implements File.StatFS.
   185  func (c *clientFile) StatFS() (FSStat, error) {
   186  	if c.closed.Load() != 0 {
   187  		return FSStat{}, unix.EBADF
   188  	}
   189  
   190  	rstatfs := Rstatfs{}
   191  	if err := c.client.sendRecv(&Tstatfs{FID: c.fid}, &rstatfs); err != nil {
   192  		return FSStat{}, err
   193  	}
   194  
   195  	return rstatfs.FSStat, nil
   196  }
   197  
   198  // FSync implements File.FSync.
   199  func (c *clientFile) FSync() error {
   200  	if c.closed.Load() != 0 {
   201  		return unix.EBADF
   202  	}
   203  
   204  	return c.client.sendRecv(&Tfsync{FID: c.fid}, &Rfsync{})
   205  }
   206  
   207  // GetAttr implements File.GetAttr.
   208  func (c *clientFile) GetAttr(req AttrMask) (QID, AttrMask, Attr, error) {
   209  	if c.closed.Load() != 0 {
   210  		return QID{}, AttrMask{}, Attr{}, unix.EBADF
   211  	}
   212  
   213  	rgetattr := Rgetattr{}
   214  	if err := c.client.sendRecv(&Tgetattr{FID: c.fid, AttrMask: req}, &rgetattr); err != nil {
   215  		return QID{}, AttrMask{}, Attr{}, err
   216  	}
   217  
   218  	return rgetattr.QID, rgetattr.Valid, rgetattr.Attr, nil
   219  }
   220  
   221  // SetAttr implements File.SetAttr.
   222  func (c *clientFile) SetAttr(valid SetAttrMask, attr SetAttr) error {
   223  	if c.closed.Load() != 0 {
   224  		return unix.EBADF
   225  	}
   226  
   227  	return c.client.sendRecv(&Tsetattr{FID: c.fid, Valid: valid, SetAttr: attr}, &Rsetattr{})
   228  }
   229  
   230  // GetXattr implements File.GetXattr.
   231  func (c *clientFile) GetXattr(name string, size uint64) (string, error) {
   232  	if c.closed.Load() != 0 {
   233  		return "", unix.EBADF
   234  	}
   235  	if !versionSupportsGetSetXattr(c.client.version) {
   236  		return "", unix.EOPNOTSUPP
   237  	}
   238  
   239  	rgetxattr := Rgetxattr{}
   240  	if err := c.client.sendRecv(&Tgetxattr{FID: c.fid, Name: name, Size: size}, &rgetxattr); err != nil {
   241  		return "", err
   242  	}
   243  
   244  	return rgetxattr.Value, nil
   245  }
   246  
   247  // SetXattr implements File.SetXattr.
   248  func (c *clientFile) SetXattr(name, value string, flags uint32) error {
   249  	if c.closed.Load() != 0 {
   250  		return unix.EBADF
   251  	}
   252  	if !versionSupportsGetSetXattr(c.client.version) {
   253  		return unix.EOPNOTSUPP
   254  	}
   255  
   256  	return c.client.sendRecv(&Tsetxattr{FID: c.fid, Name: name, Value: value, Flags: flags}, &Rsetxattr{})
   257  }
   258  
   259  // ListXattr implements File.ListXattr.
   260  func (c *clientFile) ListXattr(size uint64) (map[string]struct{}, error) {
   261  	if c.closed.Load() != 0 {
   262  		return nil, unix.EBADF
   263  	}
   264  	if !versionSupportsListRemoveXattr(c.client.version) {
   265  		return nil, unix.EOPNOTSUPP
   266  	}
   267  
   268  	rlistxattr := Rlistxattr{}
   269  	if err := c.client.sendRecv(&Tlistxattr{FID: c.fid, Size: size}, &rlistxattr); err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	xattrs := make(map[string]struct{}, len(rlistxattr.Xattrs))
   274  	for _, x := range rlistxattr.Xattrs {
   275  		xattrs[x] = struct{}{}
   276  	}
   277  	return xattrs, nil
   278  }
   279  
   280  // RemoveXattr implements File.RemoveXattr.
   281  func (c *clientFile) RemoveXattr(name string) error {
   282  	if c.closed.Load() != 0 {
   283  		return unix.EBADF
   284  	}
   285  	if !versionSupportsListRemoveXattr(c.client.version) {
   286  		return unix.EOPNOTSUPP
   287  	}
   288  
   289  	return c.client.sendRecv(&Tremovexattr{FID: c.fid, Name: name}, &Rremovexattr{})
   290  }
   291  
   292  // Allocate implements File.Allocate.
   293  func (c *clientFile) Allocate(mode AllocateMode, offset, length uint64) error {
   294  	if c.closed.Load() != 0 {
   295  		return unix.EBADF
   296  	}
   297  	if !versionSupportsTallocate(c.client.version) {
   298  		return unix.EOPNOTSUPP
   299  	}
   300  
   301  	return c.client.sendRecv(&Tallocate{FID: c.fid, Mode: mode, Offset: offset, Length: length}, &Rallocate{})
   302  }
   303  
   304  // Remove implements File.Remove.
   305  //
   306  // N.B. This method is no longer part of the file interface and should be
   307  // considered deprecated.
   308  func (c *clientFile) Remove() error {
   309  	// Avoid double close.
   310  	if !c.closed.CompareAndSwap(0, 1) {
   311  		return unix.EBADF
   312  	}
   313  
   314  	// Send the remove message.
   315  	if err := c.client.sendRecv(&Tremove{FID: c.fid}, &Rremove{}); err != nil {
   316  		return err
   317  	}
   318  
   319  	// "It is correct to consider remove to be a clunk with the side effect
   320  	// of removing the file if permissions allow."
   321  	// https://swtch.com/plan9port/man/man9/remove.html
   322  
   323  	// Return the FID to the pool.
   324  	c.client.fidPool.Put(uint64(c.fid))
   325  	return nil
   326  }
   327  
   328  // Close implements File.Close.
   329  func (c *clientFile) Close() error {
   330  	// Avoid double close.
   331  	if !c.closed.CompareAndSwap(0, 1) {
   332  		return unix.EBADF
   333  	}
   334  
   335  	// Send the close message.
   336  	if err := c.client.sendRecv(&Tclunk{FID: c.fid}, &Rclunk{}); err != nil {
   337  		// If an error occurred, we toss away the FID. This isn't ideal,
   338  		// but I'm not sure what else makes sense in this context.
   339  		log.Warningf("Tclunk failed, losing FID %v: %v", c.fid, err)
   340  		return err
   341  	}
   342  
   343  	// Return the FID to the pool.
   344  	c.client.fidPool.Put(uint64(c.fid))
   345  	return nil
   346  }
   347  
   348  // SetAttrClose implements File.SetAttrClose.
   349  func (c *clientFile) SetAttrClose(valid SetAttrMask, attr SetAttr) error {
   350  	if !versionSupportsTsetattrclunk(c.client.version) {
   351  		setAttrErr := c.SetAttr(valid, attr)
   352  
   353  		// Try to close file even in case of failure above. Since the state of the
   354  		// file is unknown to the caller, it will not attempt to close the file
   355  		// again.
   356  		if err := c.Close(); err != nil {
   357  			return err
   358  		}
   359  
   360  		return setAttrErr
   361  	}
   362  
   363  	// Avoid double close.
   364  	if !c.closed.CompareAndSwap(0, 1) {
   365  		return unix.EBADF
   366  	}
   367  
   368  	// Send the message.
   369  	if err := c.client.sendRecv(&Tsetattrclunk{FID: c.fid, Valid: valid, SetAttr: attr}, &Rsetattrclunk{}); err != nil {
   370  		// If an error occurred, we toss away the FID. This isn't ideal,
   371  		// but I'm not sure what else makes sense in this context.
   372  		log.Warningf("Tsetattrclunk failed, losing FID %v: %v", c.fid, err)
   373  		return err
   374  	}
   375  
   376  	// Return the FID to the pool.
   377  	c.client.fidPool.Put(uint64(c.fid))
   378  	return nil
   379  }
   380  
   381  // Open implements File.Open.
   382  func (c *clientFile) Open(flags OpenFlags) (*fd.FD, QID, uint32, error) {
   383  	if c.closed.Load() != 0 {
   384  		return nil, QID{}, 0, unix.EBADF
   385  	}
   386  
   387  	rlopen := Rlopen{}
   388  	if err := c.client.sendRecv(&Tlopen{FID: c.fid, Flags: flags}, &rlopen); err != nil {
   389  		return nil, QID{}, 0, err
   390  	}
   391  
   392  	return rlopen.File, rlopen.QID, rlopen.IoUnit, nil
   393  }
   394  
   395  func (c *clientFile) Bind(sockType uint32, sockName string, uid UID, gid GID) (File, QID, AttrMask, Attr, error) {
   396  	if c.closed.Load() != 0 {
   397  		return nil, QID{}, AttrMask{}, Attr{}, unix.EBADF
   398  	}
   399  
   400  	if !versionSupportsBind(c.client.version) {
   401  		return nil, QID{}, AttrMask{}, Attr{}, unix.EOPNOTSUPP
   402  	}
   403  
   404  	fid, ok := c.client.fidPool.Get()
   405  	if !ok {
   406  		return nil, QID{}, AttrMask{}, Attr{}, ErrOutOfFIDs
   407  	}
   408  
   409  	tbind := Tbind{
   410  		SockType:  sockType,
   411  		SockName:  sockName,
   412  		UID:       uid,
   413  		GID:       gid,
   414  		Directory: c.fid,
   415  		NewFID:    FID(fid),
   416  	}
   417  	rbind := Rbind{}
   418  	if err := c.client.sendRecv(&tbind, &rbind); err != nil {
   419  		c.client.fidPool.Put(fid)
   420  		return nil, QID{}, AttrMask{}, Attr{}, err
   421  	}
   422  
   423  	return c.client.newFile(FID(fid)), rbind.QID, rbind.Valid, rbind.Attr, nil
   424  }
   425  
   426  // Connect implements File.Connect.
   427  func (c *clientFile) Connect(socketType SocketType) (*fd.FD, error) {
   428  	if c.closed.Load() != 0 {
   429  		return nil, unix.EBADF
   430  	}
   431  
   432  	if !VersionSupportsConnect(c.client.version) {
   433  		return nil, unix.ECONNREFUSED
   434  	}
   435  
   436  	rlconnect := Rlconnect{}
   437  	if err := c.client.sendRecv(&Tlconnect{FID: c.fid, SocketType: socketType}, &rlconnect); err != nil {
   438  		return nil, err
   439  	}
   440  
   441  	return rlconnect.File, nil
   442  }
   443  
   444  // chunk applies fn to p in chunkSize-sized chunks until fn returns a partial result, p is
   445  // exhausted, or an error is encountered (which may be io.EOF).
   446  func chunk(chunkSize uint32, fn func([]byte, uint64) (int, error), p []byte, offset uint64) (int, error) {
   447  	// Some p9.Clients depend on executing fn on zero-byte buffers. Handle this
   448  	// as a special case (normally it is fine to short-circuit and return (0, nil)).
   449  	if len(p) == 0 {
   450  		return fn(p, offset)
   451  	}
   452  
   453  	// total is the cumulative bytes processed.
   454  	var total int
   455  	for {
   456  		var n int
   457  		var err error
   458  
   459  		// We're done, don't bother trying to do anything more.
   460  		if total == len(p) {
   461  			return total, nil
   462  		}
   463  
   464  		// Apply fn to a chunkSize-sized (or less) chunk of p.
   465  		if len(p) < total+int(chunkSize) {
   466  			n, err = fn(p[total:], offset)
   467  		} else {
   468  			n, err = fn(p[total:total+int(chunkSize)], offset)
   469  		}
   470  		total += n
   471  		offset += uint64(n)
   472  
   473  		// Return whatever we have processed if we encounter an error. This error
   474  		// could be io.EOF.
   475  		if err != nil {
   476  			return total, err
   477  		}
   478  
   479  		// Did we get a partial result? If so, return it immediately.
   480  		if n < int(chunkSize) {
   481  			return total, nil
   482  		}
   483  
   484  		// If we received more bytes than we ever requested, this is a problem.
   485  		if total > len(p) {
   486  			panic(fmt.Sprintf("bytes completed (%d)) > requested (%d)", total, len(p)))
   487  		}
   488  	}
   489  }
   490  
   491  // ReadAt proxies File.ReadAt.
   492  func (c *clientFile) ReadAt(p []byte, offset uint64) (int, error) {
   493  	return chunk(c.client.payloadSize, c.readAt, p, offset)
   494  }
   495  
   496  func (c *clientFile) readAt(p []byte, offset uint64) (int, error) {
   497  	if c.closed.Load() != 0 {
   498  		return 0, unix.EBADF
   499  	}
   500  
   501  	rread := Rread{Data: p}
   502  	if err := c.client.sendRecv(&Tread{FID: c.fid, Offset: offset, Count: uint32(len(p))}, &rread); err != nil {
   503  		return 0, err
   504  	}
   505  
   506  	// The message may have been truncated, or for some reason a new buffer
   507  	// allocated. This isn't the common path, but we make sure that if the
   508  	// payload has changed we copy it. See transport.go for more information.
   509  	if len(p) > 0 && len(rread.Data) > 0 && &rread.Data[0] != &p[0] {
   510  		copy(p, rread.Data)
   511  	}
   512  
   513  	// io.EOF is not an error that a p9 server can return. Use POSIX semantics to
   514  	// return io.EOF manually: zero bytes were returned and a non-zero buffer was used.
   515  	if len(rread.Data) == 0 && len(p) > 0 {
   516  		return 0, io.EOF
   517  	}
   518  
   519  	return len(rread.Data), nil
   520  }
   521  
   522  // WriteAt proxies File.WriteAt.
   523  func (c *clientFile) WriteAt(p []byte, offset uint64) (int, error) {
   524  	return chunk(c.client.payloadSize, c.writeAt, p, offset)
   525  }
   526  
   527  func (c *clientFile) writeAt(p []byte, offset uint64) (int, error) {
   528  	if c.closed.Load() != 0 {
   529  		return 0, unix.EBADF
   530  	}
   531  
   532  	rwrite := Rwrite{}
   533  	if err := c.client.sendRecv(&Twrite{FID: c.fid, Offset: offset, Data: p}, &rwrite); err != nil {
   534  		return 0, err
   535  	}
   536  
   537  	return int(rwrite.Count), nil
   538  }
   539  
   540  // ReadWriterFile wraps a File and implements io.ReadWriter, io.ReaderAt, and io.WriterAt.
   541  type ReadWriterFile struct {
   542  	File   File
   543  	Offset uint64
   544  }
   545  
   546  // Read implements part of the io.ReadWriter interface.
   547  func (r *ReadWriterFile) Read(p []byte) (int, error) {
   548  	n, err := r.File.ReadAt(p, r.Offset)
   549  	r.Offset += uint64(n)
   550  	if err != nil {
   551  		return n, err
   552  	}
   553  	if n == 0 && len(p) > 0 {
   554  		return n, io.EOF
   555  	}
   556  	return n, nil
   557  }
   558  
   559  // ReadAt implements the io.ReaderAt interface.
   560  func (r *ReadWriterFile) ReadAt(p []byte, offset int64) (int, error) {
   561  	n, err := r.File.ReadAt(p, uint64(offset))
   562  	if err != nil {
   563  		return 0, err
   564  	}
   565  	if n == 0 && len(p) > 0 {
   566  		return n, io.EOF
   567  	}
   568  	return n, nil
   569  }
   570  
   571  // Write implements part of the io.ReadWriter interface.
   572  //
   573  // Note that this may return a short write with a nil error. This violates the
   574  // contract of io.Writer, but is more consistent with gVisor's pattern of
   575  // returning errors that correspond to Linux errnos. Since short writes without
   576  // error are common in Linux, returning a nil error is appropriate.
   577  func (r *ReadWriterFile) Write(p []byte) (int, error) {
   578  	n, err := r.File.WriteAt(p, r.Offset)
   579  	r.Offset += uint64(n)
   580  	return n, err
   581  }
   582  
   583  // WriteAt implements the io.WriteAt interface.
   584  //
   585  // Note that this may return a short write with a nil error. This violates the
   586  // contract of io.WriterAt. See comment on Write for justification.
   587  func (r *ReadWriterFile) WriteAt(p []byte, offset int64) (int, error) {
   588  	return r.File.WriteAt(p, uint64(offset))
   589  }
   590  
   591  // Rename implements File.Rename.
   592  func (c *clientFile) Rename(dir File, name string) error {
   593  	if c.closed.Load() != 0 {
   594  		return unix.EBADF
   595  	}
   596  
   597  	clientDir, ok := dir.(*clientFile)
   598  	if !ok {
   599  		return unix.EBADF
   600  	}
   601  
   602  	return c.client.sendRecv(&Trename{FID: c.fid, Directory: clientDir.fid, Name: name}, &Rrename{})
   603  }
   604  
   605  // Create implements File.Create.
   606  func (c *clientFile) Create(name string, openFlags OpenFlags, permissions FileMode, uid UID, gid GID) (*fd.FD, File, QID, uint32, error) {
   607  	if c.closed.Load() != 0 {
   608  		return nil, nil, QID{}, 0, unix.EBADF
   609  	}
   610  
   611  	msg := Tlcreate{
   612  		FID:         c.fid,
   613  		Name:        name,
   614  		OpenFlags:   openFlags,
   615  		Permissions: permissions,
   616  		GID:         NoGID,
   617  	}
   618  
   619  	if versionSupportsTucreation(c.client.version) {
   620  		msg.GID = gid
   621  		rucreate := Rucreate{}
   622  		if err := c.client.sendRecv(&Tucreate{Tlcreate: msg, UID: uid}, &rucreate); err != nil {
   623  			return nil, nil, QID{}, 0, err
   624  		}
   625  		return rucreate.File, c, rucreate.QID, rucreate.IoUnit, nil
   626  	}
   627  
   628  	rlcreate := Rlcreate{}
   629  	if err := c.client.sendRecv(&msg, &rlcreate); err != nil {
   630  		return nil, nil, QID{}, 0, err
   631  	}
   632  
   633  	return rlcreate.File, c, rlcreate.QID, rlcreate.IoUnit, nil
   634  }
   635  
   636  // Mkdir implements File.Mkdir.
   637  func (c *clientFile) Mkdir(name string, permissions FileMode, uid UID, gid GID) (QID, error) {
   638  	if c.closed.Load() != 0 {
   639  		return QID{}, unix.EBADF
   640  	}
   641  
   642  	msg := Tmkdir{
   643  		Directory:   c.fid,
   644  		Name:        name,
   645  		Permissions: permissions,
   646  		GID:         NoGID,
   647  	}
   648  
   649  	if versionSupportsTucreation(c.client.version) {
   650  		msg.GID = gid
   651  		rumkdir := Rumkdir{}
   652  		if err := c.client.sendRecv(&Tumkdir{Tmkdir: msg, UID: uid}, &rumkdir); err != nil {
   653  			return QID{}, err
   654  		}
   655  		return rumkdir.QID, nil
   656  	}
   657  
   658  	rmkdir := Rmkdir{}
   659  	if err := c.client.sendRecv(&msg, &rmkdir); err != nil {
   660  		return QID{}, err
   661  	}
   662  
   663  	return rmkdir.QID, nil
   664  }
   665  
   666  // Symlink implements File.Symlink.
   667  func (c *clientFile) Symlink(oldname string, newname string, uid UID, gid GID) (QID, error) {
   668  	if c.closed.Load() != 0 {
   669  		return QID{}, unix.EBADF
   670  	}
   671  
   672  	msg := Tsymlink{
   673  		Directory: c.fid,
   674  		Name:      newname,
   675  		Target:    oldname,
   676  		GID:       NoGID,
   677  	}
   678  
   679  	if versionSupportsTucreation(c.client.version) {
   680  		msg.GID = gid
   681  		rusymlink := Rusymlink{}
   682  		if err := c.client.sendRecv(&Tusymlink{Tsymlink: msg, UID: uid}, &rusymlink); err != nil {
   683  			return QID{}, err
   684  		}
   685  		return rusymlink.QID, nil
   686  	}
   687  
   688  	rsymlink := Rsymlink{}
   689  	if err := c.client.sendRecv(&msg, &rsymlink); err != nil {
   690  		return QID{}, err
   691  	}
   692  
   693  	return rsymlink.QID, nil
   694  }
   695  
   696  // Link implements File.Link.
   697  func (c *clientFile) Link(target File, newname string) error {
   698  	if c.closed.Load() != 0 {
   699  		return unix.EBADF
   700  	}
   701  
   702  	targetFile, ok := target.(*clientFile)
   703  	if !ok {
   704  		return unix.EBADF
   705  	}
   706  
   707  	return c.client.sendRecv(&Tlink{Directory: c.fid, Name: newname, Target: targetFile.fid}, &Rlink{})
   708  }
   709  
   710  // Mknod implements File.Mknod.
   711  func (c *clientFile) Mknod(name string, mode FileMode, major uint32, minor uint32, uid UID, gid GID) (QID, error) {
   712  	if c.closed.Load() != 0 {
   713  		return QID{}, unix.EBADF
   714  	}
   715  
   716  	msg := Tmknod{
   717  		Directory: c.fid,
   718  		Name:      name,
   719  		Mode:      mode,
   720  		Major:     major,
   721  		Minor:     minor,
   722  		GID:       NoGID,
   723  	}
   724  
   725  	if versionSupportsTucreation(c.client.version) {
   726  		msg.GID = gid
   727  		rumknod := Rumknod{}
   728  		if err := c.client.sendRecv(&Tumknod{Tmknod: msg, UID: uid}, &rumknod); err != nil {
   729  			return QID{}, err
   730  		}
   731  		return rumknod.QID, nil
   732  	}
   733  
   734  	rmknod := Rmknod{}
   735  	if err := c.client.sendRecv(&msg, &rmknod); err != nil {
   736  		return QID{}, err
   737  	}
   738  
   739  	return rmknod.QID, nil
   740  }
   741  
   742  // RenameAt implements File.RenameAt.
   743  func (c *clientFile) RenameAt(oldname string, newdir File, newname string) error {
   744  	if c.closed.Load() != 0 {
   745  		return unix.EBADF
   746  	}
   747  
   748  	clientNewDir, ok := newdir.(*clientFile)
   749  	if !ok {
   750  		return unix.EBADF
   751  	}
   752  
   753  	return c.client.sendRecv(&Trenameat{OldDirectory: c.fid, OldName: oldname, NewDirectory: clientNewDir.fid, NewName: newname}, &Rrenameat{})
   754  }
   755  
   756  // UnlinkAt implements File.UnlinkAt.
   757  func (c *clientFile) UnlinkAt(name string, flags uint32) error {
   758  	if c.closed.Load() != 0 {
   759  		return unix.EBADF
   760  	}
   761  
   762  	return c.client.sendRecv(&Tunlinkat{Directory: c.fid, Name: name, Flags: flags}, &Runlinkat{})
   763  }
   764  
   765  // Readdir implements File.Readdir.
   766  func (c *clientFile) Readdir(direntOffset uint64, count uint32) ([]Dirent, error) {
   767  	if c.closed.Load() != 0 {
   768  		return nil, unix.EBADF
   769  	}
   770  
   771  	rreaddir := Rreaddir{}
   772  	if err := c.client.sendRecv(&Treaddir{Directory: c.fid, DirentOffset: direntOffset, Count: count}, &rreaddir); err != nil {
   773  		return nil, err
   774  	}
   775  
   776  	return rreaddir.Entries, nil
   777  }
   778  
   779  // Readlink implements File.Readlink.
   780  func (c *clientFile) Readlink() (string, error) {
   781  	if c.closed.Load() != 0 {
   782  		return "", unix.EBADF
   783  	}
   784  
   785  	rreadlink := Rreadlink{}
   786  	if err := c.client.sendRecv(&Treadlink{FID: c.fid}, &rreadlink); err != nil {
   787  		return "", err
   788  	}
   789  
   790  	return rreadlink.Target, nil
   791  }
   792  
   793  // Flush implements File.Flush.
   794  func (c *clientFile) Flush() error {
   795  	if c.closed.Load() != 0 {
   796  		return unix.EBADF
   797  	}
   798  
   799  	if !VersionSupportsTflushf(c.client.version) {
   800  		return nil
   801  	}
   802  
   803  	return c.client.sendRecv(&Tflushf{FID: c.fid}, &Rflushf{})
   804  }