| // 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 |
| } |