blob: 8f9ddfc84e5adf313c6557be7f08617f7b96b9a5 [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/fidl"
"syscall/zx/internal/context"
"syscall/zx/io"
"syscall/zx/io2"
)
var _ FDIO = (*Directory)(nil)
// Directory is a wrapper around a Node which implements FDIO.
type Directory struct {
Node
}
func NewDirectoryWithCtx(proxy *io.DirectoryAdminWithCtxInterface) *Directory {
return &Directory{Node: Node{NodeWithCtxInterface: (*io.NodeWithCtxInterface)(proxy)}}
}
func (d *Directory) getToken() (zx.Handle, error) {
status, token, err := d.directoryInterface().GetToken(context.Background())
if err := zxStatusToError(status, err, "io.directory.getToken"); err != nil {
return zx.HandleInvalid, err
}
return token, nil
}
// directoryInterface returns the underlying DirectoryAdmin FIDL client proxy.
func (d *Directory) directoryInterface() *io.DirectoryAdminWithCtxInterface {
return (*io.DirectoryAdminWithCtxInterface)(d.Node.NodeWithCtxInterface)
}
func (d *Directory) Unmount() (int32, error) {
return d.directoryInterface().Unmount(context.Background())
}
// Close closes the Directory object.
func (d *Directory) Close() error {
return d.Node.Close()
}
// 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.NewNodeWithCtxInterfaceRequest()
if err != nil {
return nil, err
}
fdio, err := func() (FDIO, error) {
if err := d.directoryInterface().Open(context.Background(), flags|io.OpenFlagDescribe, mode, pathname, req); err != nil {
return nil, err
}
status, info, err := obj.ExpectOnOpen(context.Background())
if err := zxStatusToError(status, err, "io.directory.Open"); err != nil {
return nil, err
}
return nodeFromInfo(info, obj)
}()
if err != nil {
return nil, newMultiError(((*fidl.ChannelProxy)(obj)).Close(), "closeErr", err, "innerErr")
}
return fdio, err
}
func (d *Directory) openParent(pathname string) (FDIO, string, error) {
dirpath, name := pathSplit(pathname)
parent, err := d.Open(
pathDir(dirpath),
io.OpenRightReadable|io.OpenFlagDirectory|io.OpenFlagPosix,
S_IFDIR)
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
}
err = func() error {
olddir, ok := oldparent.(*Directory)
if !ok {
return &zx.Error{Status: zx.ErrNotSupported, Text: "io.directory.Link"}
}
newparent, newname, err := d.openParent(newpath)
if err != nil {
return err
}
err = func() error {
newdir, ok := newparent.(*Directory)
if !ok {
return &zx.Error{Status: zx.ErrNotSupported, Text: "io.directory.Link"}
}
token, err := newdir.getToken()
if err != nil {
return err
}
status, err := olddir.directoryInterface().Link(context.Background(), oldname, token, newname)
return zxStatusToError(status, err, "io.directory.Link")
}()
return newMultiError(newparent.Close(), "newCloseErr", err, "innerErr")
}()
return newMultiError(oldparent.Close(), "oldCloseErr", err, "innerErr")
}
// 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
}
err = func() error {
olddir, ok := oldf.(*Directory)
if !ok {
return &zx.Error{Status: zx.ErrNotSupported, Text: "io.directory.Rename"}
}
newf, newname, err := d.openParent(newpath)
if err != nil {
return err
}
err = func() error {
newdir, ok := newf.(*Directory)
if !ok {
return &zx.Error{Status: zx.ErrNotSupported, Text: "io.directory.Rename"}
}
token, err := newdir.getToken()
if err != nil {
return err
}
status, err := olddir.directoryInterface().Rename2(
context.Background(), oldname, zx.Event(token), newname)
if err != nil {
return err
} else if status.Which() == io.DirectoryRename2ResultErr {
return zxStatusToError(status.Err, nil, "io.directory.Rename")
}
return nil
}()
return newMultiError(newf.Close(), "newCloseErr", err, "innerErr")
}()
return newMultiError(oldf.Close(), "oldCloseErr", err, "innerErr")
}
// Unlink unlinks an object at a path under this Directory.
func (d *Directory) Unlink(pathname string) error {
parent, name, err := d.openParent(pathname)
if err != nil {
return err
}
err = func() error {
parentdir, ok := parent.(*Directory)
if !ok {
return &zx.Error{Status: zx.ErrNotSupported, Text: "io.directory.Unlink"}
}
status, err := parentdir.directoryInterface().Unlink(context.Background(), name, io2.UnlinkOptions{})
return zxStatusToError(status.Err, err, "io.directory.Unlink")
}()
return newMultiError(parent.Close(), "closeErr", err, "innerErr")
}
// 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(context.Background(), max)
if err := zxStatusToError(status, err, "io.file.ReadDirents"); err != nil {
return nil, err
}
return dirents, nil
}
// Rewind resets the ReadDirents' counter.
func (d *Directory) Rewind() error {
status, err := d.directoryInterface().Rewind(context.Background())
return zxStatusToError(status, err, "io.directory.Rewind")
}
// Seek implements FDIO for Directory.
func (d *Directory) Seek(offset int64, whence int) (int64, error) {
if offset == 0 && io.SeekOrigin(whence) == io.SeekOriginStart {
return 0, d.Rewind()
}
return 0, &zx.Error{Status: zx.ErrNotSupported, Text: "io.Directory.Seek"}
}