| // Copyright 2018 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // Go's distribution tools attempt to compile everything; this file |
| // depends on types that don't compile in not-Fuchsia. |
| // +build fuchsia |
| |
| package fdio |
| |
| import ( |
| "syscall/zx" |
| "syscall/zx/internal/context" |
| "syscall/zx/io" |
| "syscall/zx/mem" |
| ) |
| |
| var _ FDIO = (*File)(nil) |
| |
| // File is a wrapper around a Node which implements FDIO. |
| type File struct { |
| Node |
| Event zx.Event |
| } |
| |
| func NewFileWithCtx(proxy *io.FileWithCtxInterface, event zx.Event) *File { |
| return &File{Node: Node{NodeWithCtxInterface: (*io.NodeWithCtxInterface)(proxy)}, Event: event} |
| } |
| |
| // fileInterface returns the underlying File FIDL client proxy. |
| func (f *File) fileInterface() *io.FileWithCtxInterface { |
| return (*io.FileWithCtxInterface)(f.Node.NodeWithCtxInterface) |
| } |
| |
| func (f *File) GetBuffer(flags uint32) (int32, *mem.Buffer, error) { |
| return f.fileInterface().GetBuffer(context.Background(), flags) |
| } |
| |
| // Handles returns all the underlying handles for this file, |
| // which is the control channel and an event. |
| func (f *File) Handles() []zx.Handle { |
| return append(f.Node.Handles(), zx.Handle(f.Event)) |
| } |
| |
| // Close closes all the control channel and event associated with the file |
| // object. |
| func (f *File) Close() error { |
| return newMultiError(f.Node.Close(), "nodeCloseErr", f.Event.Close(), "eventCloseErr") |
| } |
| |
| // read reads data from a File. If off == -1 then it does a read |
| // from whatever the seek offset is. |
| func (f *File) read(data []byte, off int64) (int, error) { |
| n := 0 |
| for n < len(data) { |
| bytesToRead := int(io.MaxBuf) |
| if len(data)-n < int(io.MaxBuf) { |
| bytesToRead = len(data) - n |
| } |
| var status int32 |
| var out []byte |
| var err error |
| if off == -1 { |
| status, out, err = f.fileInterface().Read(context.Background(), uint64(bytesToRead)) |
| } else { |
| status, out, err = f.fileInterface().ReadAt( |
| context.Background(), |
| uint64(bytesToRead), |
| uint64(int64(n)+off), |
| ) |
| } |
| if err := zxStatusToError(status, err, ""); err != nil { |
| return n, err |
| } |
| if len(out) == 0 { |
| return n, nil |
| } |
| n += copy(data[n:], out) |
| // Stop at short read. |
| if len(out) < bytesToRead { |
| return n, nil |
| } |
| } |
| return n, nil |
| } |
| |
| // Read reads data from the internally held offset in the File. |
| func (f *File) Read(data []byte) (int, error) { |
| return f.read(data, -1) |
| } |
| |
| // ReadAt reads data at an offset in a File. |
| func (f *File) ReadAt(data []byte, off int64) (int, error) { |
| return f.read(data, off) |
| } |
| |
| // write writes data to a file. If off == -1 then the write occurs at |
| // whatever the seek offset is. |
| func (f *File) write(data []byte, off int64) (int, error) { |
| n := 0 |
| for n < len(data) { |
| bytesToWrite := int(io.MaxBuf) |
| if len(data)-n < int(io.MaxBuf) { |
| bytesToWrite = len(data) - n |
| } |
| var status int32 |
| var written uint64 |
| var err error |
| if off == -1 { |
| status, written, err = f.fileInterface().Write( |
| context.Background(), |
| data[n:][:bytesToWrite], |
| ) |
| } else { |
| status, written, err = f.fileInterface().WriteAt( |
| context.Background(), |
| data[n:][:bytesToWrite], |
| uint64(int64(n)+off), |
| ) |
| } |
| if err := zxStatusToError(status, err, ""); err != nil { |
| return n, err |
| } |
| n += int(written) |
| // Error on a short write. |
| if int(written) < bytesToWrite { |
| return n, &zx.Error{Status: zx.ErrIO, Text: "io.file"} |
| } |
| } |
| return n, nil |
| } |
| |
| // Write writes data at the internally held offset in the File. |
| func (f *File) Write(data []byte) (int, error) { |
| return f.write(data, -1) |
| } |
| |
| // WriteAt writes data at an offset in a File. |
| func (f *File) WriteAt(data []byte, off int64) (int, error) { |
| return f.write(data, off) |
| } |
| |
| // Seek moves the read/write head offset of the File. |
| func (f *File) Seek(offset int64, whence int) (int64, error) { |
| status, off, err := f.fileInterface().Seek(context.Background(), offset, io.SeekOrigin(whence)) |
| if err := zxStatusToError(status, err, "io.file.Seek"); err != nil { |
| return -1, err |
| } |
| return int64(off), nil |
| } |
| |
| // Truncate truncates a File. |
| func (f *File) Truncate(length uint64) error { |
| status, err := f.fileInterface().Truncate(context.Background(), length) |
| return zxStatusToError(status, err, "io.file.Truncate") |
| } |