blob: 1b0dcd4edb9a144354c8fcd28a61108f19a0d824 [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 (
"path"
"syscall/zx"
"syscall/zx/fidl"
"syscall/zx/io"
)
// Directory is a wrapper around a DirectoryInterface which implements FDIO.
type Directory struct {
Node
}
func (d *Directory) getToken() (zx.Handle, error) {
status, token, err := d.DirectoryInterface().GetToken()
if err != nil {
return zx.HandleInvalid, err
} else if status != zx.ErrOk {
return zx.HandleInvalid, zx.Error{Status: status, Text: "io.directory"}
}
return token, nil
}
// DirectoryInterface returns the underlying Directory FIDL interface.
func (d *Directory) DirectoryInterface() *io.DirectoryInterface {
return (*io.DirectoryInterface)(d.Node.Object.ObjectInterface)
}
// Close closes the Directory object.
func (d *Directory) Close() error {
defer ((*fidl.Proxy)(d.Node.Object.ObjectInterface)).Close()
if status, err := d.Node.Object.ObjectInterface.Close(); err != nil {
return err
} else if status != zx.ErrOk {
return zx.Error{Status: status, Text: "io.directory"}
}
return nil
}
// Open opens an FDIO at path with the given flags and mode relative to this
// Directory.
func (d *Directory) Open(pathname string, flags uint32, mode uint32) (FDIO, error) {
req, obj, err := io.NewObjectInterfaceRequest()
if err != nil {
return nil, err
}
err = d.DirectoryInterface().Open(flags|io.KOpenFlagDescribe, mode, pathname, req)
if err != nil {
return nil, err
}
status, info, err := obj.ExpectOnOpen()
if err != nil {
return nil, err
} else if status != zx.ErrOk {
return nil, zx.Error{Status: status, Text: "io.directory"}
}
return objectFromInfo(info, obj)
}
func (d *Directory) openParent(pathname string) (FDIO, string, error) {
dirpath, name := path.Split(pathname)
parent, err := d.Open(path.Dir(dirpath), io.KOpenRightReadable, io.KOpenFlagDirectory)
if err != nil {
return nil, "", err
}
return parent, name, err
}
// Link creates a link between two paths under this Directory.
func (d *Directory) Link(oldpath, newpath string) error {
oldparent, oldname, err := d.openParent(oldpath)
if err != nil {
return err
}
defer oldparent.Close()
newparent, newname, err := d.openParent(newpath)
if err != nil {
return err
}
defer newparent.Close()
olddir, ok := oldparent.(*Directory)
if !ok {
return zx.Error{Status: zx.ErrNotSupported, Text: "io.directory"}
}
newdir, ok := newparent.(*Directory)
if !ok {
return zx.Error{Status: zx.ErrNotSupported, Text: "io.directory"}
}
token, err := newdir.getToken()
if err != nil {
return err
}
status, err := olddir.DirectoryInterface().Link(oldname, token, newname)
if err != nil {
return err
} else if status != zx.ErrOk {
return zx.Error{Status: status, Text: "io.directory"}
}
return nil
}
// Rename renames an object at one path to another under this Directory.
func (d *Directory) Rename(oldpath, newpath string) error {
oldf, oldname, err := d.openParent(oldpath)
if err != nil {
return err
}
defer oldf.Close()
newf, newname, err := d.openParent(newpath)
if err != nil {
return err
}
defer newf.Close()
olddir, ok := oldf.(*Directory)
if !ok {
return zx.Error{Status: zx.ErrNotSupported, Text: "io.directory"}
}
newdir, ok := newf.(*Directory)
if !ok {
return zx.Error{Status: zx.ErrNotSupported, Text: "io.directory"}
}
token, err := newdir.getToken()
if err != nil {
return err
}
status, err := olddir.DirectoryInterface().Rename(oldname, token, newname)
if err != nil {
return err
} else if status != zx.ErrOk {
return zx.Error{Status: status, Text: "io.directory"}
}
return nil
}
// Unlink unlinks an object at a pauth under this Directory.
func (d *Directory) Unlink(pathname string) error {
parent, name, err := d.openParent(pathname)
if err != nil {
return err
}
defer parent.Close()
parentdir, ok := parent.(*Directory)
if !ok {
return zx.Error{Status: zx.ErrNotSupported, Text: "io.directory"}
}
if status, err := parentdir.DirectoryInterface().Unlink(name); err != nil {
return err
} else if status != zx.ErrOk {
return zx.Error{Status: status, Text: "io.directory"}
}
return nil
}
// ReadDirents returns up to max-worth bytes of byte-encoded dirents which represent
// objects under this Directory. Repeated calls to ReadDirents continues giving back
// objects.
func (d *Directory) ReadDirents(max uint64) ([]byte, error) {
status, dirents, err := d.DirectoryInterface().ReadDirents(max)
if err != nil {
return nil, err
} else if status != zx.ErrOk {
return nil, zx.Error{Status: status, Text: "io.file"}
}
return dirents, nil
}
// Rewind resets the ReadDirents' counter.
func (d *Directory) Rewind() error {
if status, err := d.DirectoryInterface().Rewind(); err != nil {
return err
} else if status != zx.ErrOk {
return zx.Error{Status: status, Text: "io.directory"}
}
return nil
}