blob: 604f81fdbdd60380c8c20e4ca38bede90a9faa49 [file] [log] [blame]
// 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")
}