2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
package p9
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"sync/atomic"
|
|
|
|
"syscall"
|
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/fd"
|
|
|
|
"gvisor.dev/gvisor/pkg/log"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Attach attaches to a server.
|
|
|
|
//
|
|
|
|
// Note that authentication is not currently supported.
|
|
|
|
func (c *Client) Attach(name string) (File, error) {
|
|
|
|
fid, ok := c.fidPool.Get()
|
|
|
|
if !ok {
|
|
|
|
return nil, ErrOutOfFIDs
|
|
|
|
}
|
|
|
|
|
|
|
|
rattach := Rattach{}
|
|
|
|
if err := c.sendRecv(&Tattach{FID: FID(fid), Auth: Tauth{AttachName: name, AuthenticationFID: NoFID, UID: NoUID}}, &rattach); err != nil {
|
|
|
|
c.fidPool.Put(fid)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.newFile(FID(fid)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// newFile returns a new client file.
|
|
|
|
func (c *Client) newFile(fid FID) *clientFile {
|
2019-11-15 19:39:25 +00:00
|
|
|
return &clientFile{
|
2018-04-27 17:37:02 +00:00
|
|
|
client: c,
|
|
|
|
fid: fid,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// clientFile is provided to clients.
|
|
|
|
//
|
|
|
|
// This proxies all of the interfaces found in file.go.
|
|
|
|
type clientFile struct {
|
|
|
|
// client is the originating client.
|
|
|
|
client *Client
|
|
|
|
|
|
|
|
// fid is the FID for this file.
|
|
|
|
fid FID
|
|
|
|
|
|
|
|
// closed indicates whether this file has been closed.
|
|
|
|
closed uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
// Walk implements File.Walk.
|
|
|
|
func (c *clientFile) Walk(names []string) ([]QID, File, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return nil, nil, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
fid, ok := c.client.fidPool.Get()
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, ErrOutOfFIDs
|
|
|
|
}
|
|
|
|
|
|
|
|
rwalk := Rwalk{}
|
|
|
|
if err := c.client.sendRecv(&Twalk{FID: c.fid, NewFID: FID(fid), Names: names}, &rwalk); err != nil {
|
|
|
|
c.client.fidPool.Put(fid)
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return a new client file.
|
|
|
|
return rwalk.QIDs, c.client.newFile(FID(fid)), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// WalkGetAttr implements File.WalkGetAttr.
|
|
|
|
func (c *clientFile) WalkGetAttr(components []string) ([]QID, File, AttrMask, Attr, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return nil, nil, AttrMask{}, Attr{}, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
if !versionSupportsTwalkgetattr(c.client.version) {
|
|
|
|
qids, file, err := c.Walk(components)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, AttrMask{}, Attr{}, err
|
|
|
|
}
|
|
|
|
_, valid, attr, err := file.GetAttr(AttrMaskAll())
|
|
|
|
if err != nil {
|
|
|
|
file.Close()
|
|
|
|
return nil, nil, AttrMask{}, Attr{}, err
|
|
|
|
}
|
|
|
|
return qids, file, valid, attr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
fid, ok := c.client.fidPool.Get()
|
|
|
|
if !ok {
|
|
|
|
return nil, nil, AttrMask{}, Attr{}, ErrOutOfFIDs
|
|
|
|
}
|
|
|
|
|
|
|
|
rwalkgetattr := Rwalkgetattr{}
|
|
|
|
if err := c.client.sendRecv(&Twalkgetattr{FID: c.fid, NewFID: FID(fid), Names: components}, &rwalkgetattr); err != nil {
|
|
|
|
c.client.fidPool.Put(fid)
|
|
|
|
return nil, nil, AttrMask{}, Attr{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return a new client file.
|
|
|
|
return rwalkgetattr.QIDs, c.client.newFile(FID(fid)), rwalkgetattr.Valid, rwalkgetattr.Attr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StatFS implements File.StatFS.
|
|
|
|
func (c *clientFile) StatFS() (FSStat, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return FSStat{}, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
rstatfs := Rstatfs{}
|
|
|
|
if err := c.client.sendRecv(&Tstatfs{FID: c.fid}, &rstatfs); err != nil {
|
|
|
|
return FSStat{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rstatfs.FSStat, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// FSync implements File.FSync.
|
|
|
|
func (c *clientFile) FSync() error {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return c.client.sendRecv(&Tfsync{FID: c.fid}, &Rfsync{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAttr implements File.GetAttr.
|
|
|
|
func (c *clientFile) GetAttr(req AttrMask) (QID, AttrMask, Attr, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return QID{}, AttrMask{}, Attr{}, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
rgetattr := Rgetattr{}
|
|
|
|
if err := c.client.sendRecv(&Tgetattr{FID: c.fid, AttrMask: req}, &rgetattr); err != nil {
|
|
|
|
return QID{}, AttrMask{}, Attr{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rgetattr.QID, rgetattr.Valid, rgetattr.Attr, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetAttr implements File.SetAttr.
|
|
|
|
func (c *clientFile) SetAttr(valid SetAttrMask, attr SetAttr) error {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return c.client.sendRecv(&Tsetattr{FID: c.fid, Valid: valid, SetAttr: attr}, &Rsetattr{})
|
|
|
|
}
|
|
|
|
|
2020-01-16 20:33:07 +00:00
|
|
|
// GetXattr implements File.GetXattr.
|
|
|
|
func (c *clientFile) GetXattr(name string, size uint64) (string, error) {
|
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return "", syscall.EBADF
|
|
|
|
}
|
2020-01-16 22:25:56 +00:00
|
|
|
if !versionSupportsGetSetXattr(c.client.version) {
|
|
|
|
return "", syscall.EOPNOTSUPP
|
|
|
|
}
|
2020-01-16 20:33:07 +00:00
|
|
|
|
|
|
|
rgetxattr := Rgetxattr{}
|
|
|
|
if err := c.client.sendRecv(&Tgetxattr{FID: c.fid, Name: name, Size: size}, &rgetxattr); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rgetxattr.Value, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetXattr implements File.SetXattr.
|
|
|
|
func (c *clientFile) SetXattr(name, value string, flags uint32) error {
|
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
2020-01-16 22:25:56 +00:00
|
|
|
if !versionSupportsGetSetXattr(c.client.version) {
|
|
|
|
return syscall.EOPNOTSUPP
|
|
|
|
}
|
2020-01-16 20:33:07 +00:00
|
|
|
|
|
|
|
return c.client.sendRecv(&Tsetxattr{FID: c.fid, Name: name, Value: value, Flags: flags}, &Rsetxattr{})
|
|
|
|
}
|
|
|
|
|
2020-02-07 22:46:24 +00:00
|
|
|
// ListXattr implements File.ListXattr.
|
|
|
|
func (c *clientFile) ListXattr(size uint64) (map[string]struct{}, error) {
|
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return nil, syscall.EBADF
|
|
|
|
}
|
|
|
|
if !versionSupportsListRemoveXattr(c.client.version) {
|
|
|
|
return nil, syscall.EOPNOTSUPP
|
|
|
|
}
|
|
|
|
|
|
|
|
rlistxattr := Rlistxattr{}
|
|
|
|
if err := c.client.sendRecv(&Tlistxattr{FID: c.fid, Size: size}, &rlistxattr); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
xattrs := make(map[string]struct{}, len(rlistxattr.Xattrs))
|
|
|
|
for _, x := range rlistxattr.Xattrs {
|
|
|
|
xattrs[x] = struct{}{}
|
|
|
|
}
|
|
|
|
return xattrs, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RemoveXattr implements File.RemoveXattr.
|
|
|
|
func (c *clientFile) RemoveXattr(name string) error {
|
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
if !versionSupportsListRemoveXattr(c.client.version) {
|
|
|
|
return syscall.EOPNOTSUPP
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.client.sendRecv(&Tremovexattr{FID: c.fid, Name: name}, &Rremovexattr{})
|
|
|
|
}
|
|
|
|
|
2019-05-09 22:34:44 +00:00
|
|
|
// Allocate implements File.Allocate.
|
|
|
|
func (c *clientFile) Allocate(mode AllocateMode, offset, length uint64) error {
|
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
if !versionSupportsTallocate(c.client.version) {
|
|
|
|
return syscall.EOPNOTSUPP
|
|
|
|
}
|
|
|
|
|
|
|
|
return c.client.sendRecv(&Tallocate{FID: c.fid, Mode: mode, Offset: offset, Length: length}, &Rallocate{})
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// Remove implements File.Remove.
|
2018-10-23 07:19:11 +00:00
|
|
|
//
|
|
|
|
// N.B. This method is no longer part of the file interface and should be
|
|
|
|
// considered deprecated.
|
2018-04-27 17:37:02 +00:00
|
|
|
func (c *clientFile) Remove() error {
|
2018-10-11 20:22:54 +00:00
|
|
|
// Avoid double close.
|
|
|
|
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
|
|
|
|
// Send the remove message.
|
2018-04-27 17:37:02 +00:00
|
|
|
if err := c.client.sendRecv(&Tremove{FID: c.fid}, &Rremove{}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// "It is correct to consider remove to be a clunk with the side effect
|
|
|
|
// of removing the file if permissions allow."
|
|
|
|
// https://swtch.com/plan9port/man/man9/remove.html
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// Return the FID to the pool.
|
|
|
|
c.client.fidPool.Put(uint64(c.fid))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Close implements File.Close.
|
|
|
|
func (c *clientFile) Close() error {
|
|
|
|
// Avoid double close.
|
|
|
|
if !atomic.CompareAndSwapUint32(&c.closed, 0, 1) {
|
2018-10-11 20:22:54 +00:00
|
|
|
return syscall.EBADF
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Send the close message.
|
|
|
|
if err := c.client.sendRecv(&Tclunk{FID: c.fid}, &Rclunk{}); err != nil {
|
|
|
|
// If an error occurred, we toss away the FID. This isn't ideal,
|
|
|
|
// but I'm not sure what else makes sense in this context.
|
|
|
|
log.Warningf("Tclunk failed, losing FID %v: %v", c.fid, err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the FID to the pool.
|
|
|
|
c.client.fidPool.Put(uint64(c.fid))
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Open implements File.Open.
|
|
|
|
func (c *clientFile) Open(flags OpenFlags) (*fd.FD, QID, uint32, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return nil, QID{}, 0, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
rlopen := Rlopen{}
|
|
|
|
if err := c.client.sendRecv(&Tlopen{FID: c.fid, Flags: flags}, &rlopen); err != nil {
|
|
|
|
return nil, QID{}, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rlopen.File, rlopen.QID, rlopen.IoUnit, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Connect implements File.Connect.
|
|
|
|
func (c *clientFile) Connect(flags ConnectFlags) (*fd.FD, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return nil, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
if !VersionSupportsConnect(c.client.version) {
|
|
|
|
return nil, syscall.ECONNREFUSED
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
rlconnect := Rlconnect{}
|
|
|
|
if err := c.client.sendRecv(&Tlconnect{FID: c.fid, Flags: flags}, &rlconnect); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rlconnect.File, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// chunk applies fn to p in chunkSize-sized chunks until fn returns a partial result, p is
|
|
|
|
// exhausted, or an error is encountered (which may be io.EOF).
|
|
|
|
func chunk(chunkSize uint32, fn func([]byte, uint64) (int, error), p []byte, offset uint64) (int, error) {
|
|
|
|
// Some p9.Clients depend on executing fn on zero-byte buffers. Handle this
|
|
|
|
// as a special case (normally it is fine to short-circuit and return (0, nil)).
|
|
|
|
if len(p) == 0 {
|
|
|
|
return fn(p, offset)
|
|
|
|
}
|
|
|
|
|
|
|
|
// total is the cumulative bytes processed.
|
|
|
|
var total int
|
|
|
|
for {
|
|
|
|
var n int
|
|
|
|
var err error
|
|
|
|
|
|
|
|
// We're done, don't bother trying to do anything more.
|
|
|
|
if total == len(p) {
|
|
|
|
return total, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply fn to a chunkSize-sized (or less) chunk of p.
|
|
|
|
if len(p) < total+int(chunkSize) {
|
|
|
|
n, err = fn(p[total:], offset)
|
|
|
|
} else {
|
|
|
|
n, err = fn(p[total:total+int(chunkSize)], offset)
|
|
|
|
}
|
|
|
|
total += n
|
|
|
|
offset += uint64(n)
|
|
|
|
|
|
|
|
// Return whatever we have processed if we encounter an error. This error
|
|
|
|
// could be io.EOF.
|
|
|
|
if err != nil {
|
|
|
|
return total, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Did we get a partial result? If so, return it immediately.
|
|
|
|
if n < int(chunkSize) {
|
|
|
|
return total, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we received more bytes than we ever requested, this is a problem.
|
|
|
|
if total > len(p) {
|
|
|
|
panic(fmt.Sprintf("bytes completed (%d)) > requested (%d)", total, len(p)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadAt proxies File.ReadAt.
|
|
|
|
func (c *clientFile) ReadAt(p []byte, offset uint64) (int, error) {
|
|
|
|
return chunk(c.client.payloadSize, c.readAt, p, offset)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *clientFile) readAt(p []byte, offset uint64) (int, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return 0, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
rread := Rread{Data: p}
|
|
|
|
if err := c.client.sendRecv(&Tread{FID: c.fid, Offset: offset, Count: uint32(len(p))}, &rread); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// The message may have been truncated, or for some reason a new buffer
|
|
|
|
// allocated. This isn't the common path, but we make sure that if the
|
|
|
|
// payload has changed we copy it. See transport.go for more information.
|
|
|
|
if len(p) > 0 && len(rread.Data) > 0 && &rread.Data[0] != &p[0] {
|
|
|
|
copy(p, rread.Data)
|
|
|
|
}
|
|
|
|
|
|
|
|
// io.EOF is not an error that a p9 server can return. Use POSIX semantics to
|
|
|
|
// return io.EOF manually: zero bytes were returned and a non-zero buffer was used.
|
|
|
|
if len(rread.Data) == 0 && len(p) > 0 {
|
|
|
|
return 0, io.EOF
|
|
|
|
}
|
|
|
|
|
|
|
|
return len(rread.Data), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteAt proxies File.WriteAt.
|
|
|
|
func (c *clientFile) WriteAt(p []byte, offset uint64) (int, error) {
|
|
|
|
return chunk(c.client.payloadSize, c.writeAt, p, offset)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *clientFile) writeAt(p []byte, offset uint64) (int, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return 0, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
rwrite := Rwrite{}
|
|
|
|
if err := c.client.sendRecv(&Twrite{FID: c.fid, Offset: offset, Data: p}, &rwrite); err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return int(rwrite.Count), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadWriterFile wraps a File and implements io.ReadWriter, io.ReaderAt, and io.WriterAt.
|
|
|
|
type ReadWriterFile struct {
|
|
|
|
File File
|
|
|
|
Offset uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read implements part of the io.ReadWriter interface.
|
|
|
|
func (r *ReadWriterFile) Read(p []byte) (int, error) {
|
|
|
|
n, err := r.File.ReadAt(p, r.Offset)
|
|
|
|
r.Offset += uint64(n)
|
|
|
|
if err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
if n == 0 && len(p) > 0 {
|
|
|
|
return n, io.EOF
|
|
|
|
}
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadAt implements the io.ReaderAt interface.
|
|
|
|
func (r *ReadWriterFile) ReadAt(p []byte, offset int64) (int, error) {
|
|
|
|
n, err := r.File.ReadAt(p, uint64(offset))
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
if n == 0 && len(p) > 0 {
|
|
|
|
return n, io.EOF
|
|
|
|
}
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write implements part of the io.ReadWriter interface.
|
|
|
|
func (r *ReadWriterFile) Write(p []byte) (int, error) {
|
|
|
|
n, err := r.File.WriteAt(p, r.Offset)
|
|
|
|
r.Offset += uint64(n)
|
|
|
|
if err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
if n < len(p) {
|
|
|
|
return n, io.ErrShortWrite
|
|
|
|
}
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// WriteAt implements the io.WriteAt interface.
|
|
|
|
func (r *ReadWriterFile) WriteAt(p []byte, offset int64) (int, error) {
|
|
|
|
n, err := r.File.WriteAt(p, uint64(offset))
|
|
|
|
if err != nil {
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
if n < len(p) {
|
|
|
|
return n, io.ErrShortWrite
|
|
|
|
}
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rename implements File.Rename.
|
|
|
|
func (c *clientFile) Rename(dir File, name string) error {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
clientDir, ok := dir.(*clientFile)
|
|
|
|
if !ok {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return c.client.sendRecv(&Trename{FID: c.fid, Directory: clientDir.fid, Name: name}, &Rrename{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create implements File.Create.
|
|
|
|
func (c *clientFile) Create(name string, openFlags OpenFlags, permissions FileMode, uid UID, gid GID) (*fd.FD, File, QID, uint32, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return nil, nil, QID{}, 0, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
msg := Tlcreate{
|
|
|
|
FID: c.fid,
|
|
|
|
Name: name,
|
|
|
|
OpenFlags: openFlags,
|
|
|
|
Permissions: permissions,
|
|
|
|
GID: NoGID,
|
|
|
|
}
|
|
|
|
|
|
|
|
if versionSupportsTucreation(c.client.version) {
|
|
|
|
msg.GID = gid
|
|
|
|
rucreate := Rucreate{}
|
|
|
|
if err := c.client.sendRecv(&Tucreate{Tlcreate: msg, UID: uid}, &rucreate); err != nil {
|
|
|
|
return nil, nil, QID{}, 0, err
|
|
|
|
}
|
|
|
|
return rucreate.File, c, rucreate.QID, rucreate.IoUnit, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
rlcreate := Rlcreate{}
|
|
|
|
if err := c.client.sendRecv(&msg, &rlcreate); err != nil {
|
|
|
|
return nil, nil, QID{}, 0, err
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return rlcreate.File, c, rlcreate.QID, rlcreate.IoUnit, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mkdir implements File.Mkdir.
|
|
|
|
func (c *clientFile) Mkdir(name string, permissions FileMode, uid UID, gid GID) (QID, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return QID{}, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
msg := Tmkdir{
|
|
|
|
Directory: c.fid,
|
|
|
|
Name: name,
|
|
|
|
Permissions: permissions,
|
|
|
|
GID: NoGID,
|
|
|
|
}
|
|
|
|
|
|
|
|
if versionSupportsTucreation(c.client.version) {
|
|
|
|
msg.GID = gid
|
|
|
|
rumkdir := Rumkdir{}
|
|
|
|
if err := c.client.sendRecv(&Tumkdir{Tmkdir: msg, UID: uid}, &rumkdir); err != nil {
|
|
|
|
return QID{}, err
|
|
|
|
}
|
|
|
|
return rumkdir.QID, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
rmkdir := Rmkdir{}
|
|
|
|
if err := c.client.sendRecv(&msg, &rmkdir); err != nil {
|
|
|
|
return QID{}, err
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return rmkdir.QID, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Symlink implements File.Symlink.
|
|
|
|
func (c *clientFile) Symlink(oldname string, newname string, uid UID, gid GID) (QID, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return QID{}, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
msg := Tsymlink{
|
|
|
|
Directory: c.fid,
|
|
|
|
Name: newname,
|
|
|
|
Target: oldname,
|
|
|
|
GID: NoGID,
|
|
|
|
}
|
|
|
|
|
|
|
|
if versionSupportsTucreation(c.client.version) {
|
|
|
|
msg.GID = gid
|
|
|
|
rusymlink := Rusymlink{}
|
|
|
|
if err := c.client.sendRecv(&Tusymlink{Tsymlink: msg, UID: uid}, &rusymlink); err != nil {
|
|
|
|
return QID{}, err
|
|
|
|
}
|
|
|
|
return rusymlink.QID, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
rsymlink := Rsymlink{}
|
|
|
|
if err := c.client.sendRecv(&msg, &rsymlink); err != nil {
|
|
|
|
return QID{}, err
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return rsymlink.QID, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Link implements File.Link.
|
|
|
|
func (c *clientFile) Link(target File, newname string) error {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
targetFile, ok := target.(*clientFile)
|
|
|
|
if !ok {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return c.client.sendRecv(&Tlink{Directory: c.fid, Name: newname, Target: targetFile.fid}, &Rlink{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mknod implements File.Mknod.
|
2019-05-20 23:52:03 +00:00
|
|
|
func (c *clientFile) Mknod(name string, mode FileMode, major uint32, minor uint32, uid UID, gid GID) (QID, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return QID{}, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
msg := Tmknod{
|
2019-05-20 23:52:03 +00:00
|
|
|
Directory: c.fid,
|
|
|
|
Name: name,
|
|
|
|
Mode: mode,
|
|
|
|
Major: major,
|
|
|
|
Minor: minor,
|
|
|
|
GID: NoGID,
|
2018-04-27 17:37:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if versionSupportsTucreation(c.client.version) {
|
|
|
|
msg.GID = gid
|
|
|
|
rumknod := Rumknod{}
|
|
|
|
if err := c.client.sendRecv(&Tumknod{Tmknod: msg, UID: uid}, &rumknod); err != nil {
|
|
|
|
return QID{}, err
|
|
|
|
}
|
|
|
|
return rumknod.QID, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
rmknod := Rmknod{}
|
|
|
|
if err := c.client.sendRecv(&msg, &rmknod); err != nil {
|
|
|
|
return QID{}, err
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return rmknod.QID, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// RenameAt implements File.RenameAt.
|
|
|
|
func (c *clientFile) RenameAt(oldname string, newdir File, newname string) error {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
clientNewDir, ok := newdir.(*clientFile)
|
|
|
|
if !ok {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return c.client.sendRecv(&Trenameat{OldDirectory: c.fid, OldName: oldname, NewDirectory: clientNewDir.fid, NewName: newname}, &Rrenameat{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnlinkAt implements File.UnlinkAt.
|
|
|
|
func (c *clientFile) UnlinkAt(name string, flags uint32) error {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return c.client.sendRecv(&Tunlinkat{Directory: c.fid, Name: name, Flags: flags}, &Runlinkat{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Readdir implements File.Readdir.
|
|
|
|
func (c *clientFile) Readdir(offset uint64, count uint32) ([]Dirent, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return nil, syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
rreaddir := Rreaddir{}
|
|
|
|
if err := c.client.sendRecv(&Treaddir{Directory: c.fid, Offset: offset, Count: count}, &rreaddir); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rreaddir.Entries, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Readlink implements File.Readlink.
|
|
|
|
func (c *clientFile) Readlink() (string, error) {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return "", syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
rreadlink := Rreadlink{}
|
|
|
|
if err := c.client.sendRecv(&Treadlink{FID: c.fid}, &rreadlink); err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
return rreadlink.Target, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Flush implements File.Flush.
|
|
|
|
func (c *clientFile) Flush() error {
|
2018-10-11 20:22:54 +00:00
|
|
|
if atomic.LoadUint32(&c.closed) != 0 {
|
|
|
|
return syscall.EBADF
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
if !VersionSupportsTflushf(c.client.version) {
|
|
|
|
return nil
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
return c.client.sendRecv(&Tflushf{FID: c.fid}, &Rflushf{})
|
|
|
|
}
|
2018-10-11 20:22:54 +00:00
|
|
|
|
|
|
|
// Renamed implements File.Renamed.
|
|
|
|
func (c *clientFile) Renamed(newDir File, newName string) {}
|