github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/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 "golang.org/x/sys/unix" 23 "github.com/nicocha30/gvisor-ligolo/pkg/atomicbitops" 24 "github.com/nicocha30/gvisor-ligolo/pkg/fd" 25 "github.com/nicocha30/gvisor-ligolo/pkg/log" 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 }