| // 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.NodeInterface) |
| } |
| |
| // 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.MaxBuf) |
| if len(data)-ptr < int(io.MaxBuf) { |
| bytesToRead = len(data) - ptr |
| } |
| var status int32 |
| 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 zx.Status(status) != zx.ErrOk { |
| return ptr, zx.Error{Status: zx.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.MaxBuf) |
| if len(data)-ptr < int(io.MaxBuf) { |
| bytesToWrite = len(data) - ptr |
| } |
| var status int32 |
| 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 zx.Status(status) != zx.ErrOk { |
| return ptr, zx.Error{Status: zx.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 zx.Status(status) != zx.ErrOk { |
| return -1, zx.Error{Status: zx.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 zx.Status(status) != zx.ErrOk { |
| return zx.Error{Status: zx.Status(status), Text: "io.file"} |
| } |
| return nil |
| } |