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