blob: 2d847d06d03fe9626ad9f45b702ec631af6e2f7a [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.
// +build fuchsia
package fdio
import (
"syscall/zx"
"syscall/zx/io"
)
// File is a wrapper around a FileInterface which implements FDIO.
type File struct {
Node
Event zx.Event
}
// FileInterface returns the underlying File FIDL interface.
func (f *File) FileInterface() *io.FileInterface {
return (*io.FileInterface)(f.Node.Object.ObjectInterface)
}
// 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 {
defer f.Event.Close()
return f.Node.Close()
}
// 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) {
ptr := 0
for ptr < len(data) {
bytesToRead := int(io.KMaxBuf)
if len(data)-ptr < int(io.KMaxBuf) {
bytesToRead = len(data) - ptr
}
var status zx.Status
var out []byte
var err error
if off == -1 {
status, out, err = f.FileInterface().Read(uint64(bytesToRead))
} else {
status, out, err = f.FileInterface().ReadAt(
uint64(bytesToRead),
uint64(int64(ptr)+off),
)
}
if err != nil {
return ptr, err
} else if status != zx.ErrOk {
return ptr, zx.Error{Status: status, Text: "io.file"}
}
if len(out) == 0 {
return ptr, nil
}
copy(data[ptr:ptr+len(out)], out)
ptr += len(out)
// Stop at short read.
if len(out) < bytesToRead {
return ptr, nil
}
}
return ptr, 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) {
ptr := 0
for ptr < len(data) {
bytesToWrite := int(io.KMaxBuf)
if len(data)-ptr < int(io.KMaxBuf) {
bytesToWrite = len(data) - ptr
}
var status zx.Status
var written uint64
var err error
if off == -1 {
status, written, err = f.FileInterface().Write(
data[ptr : ptr+bytesToWrite],
)
} else {
status, written, err = f.FileInterface().WriteAt(
data[ptr:ptr+bytesToWrite],
uint64(int64(ptr)+off),
)
}
if err != nil {
return ptr, err
} else if status != zx.ErrOk {
return ptr, zx.Error{Status: status, Text: "io.file"}
}
ptr += int(written)
// Error on a short write.
if int(written) < bytesToWrite {
return ptr, zx.Error{Status: zx.ErrIO, Text: "io.file"}
}
}
return ptr, 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(offset, io.SeekOrigin(whence))
if err != nil {
return -1, err
} else if status != zx.ErrOk {
return -1, zx.Error{Status: status, Text: "io.file"}
}
return int64(off), nil
}
// Truncate truncates a File.
func (f *File) Truncate(length uint64) error {
status, err := f.FileInterface().Truncate(length)
if err != nil {
return err
} else if status != zx.ErrOk {
return zx.Error{Status: status, Text: "io.file"}
}
return nil
}