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 }