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