blob: 5fe3ff0e7f31ac1089086bf7903ce00210a6734e [file] [log] [blame]
// Copyright 2017 The Fuchsia 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 pkgfs
import (
"fmt"
"io"
"os"
"sync"
"syscall"
"syscall/zx"
"syscall/zx/fdio"
"thinfs/fs"
"thinfs/zircon/rpc"
)
// mountInfo is a platform specific type that carries platform specific mounting
// data, such as the file descriptor or handle of the mount.
type mountInfo struct {
unmountOnce sync.Once
serveChannel *zx.Channel
parentFd *os.File
}
// Mount attaches the filesystem host to the given path. If an error occurs
// during setup, this method returns that error. If an error occurs after
// serving has started, the error causes a log.Fatal. If the given path does not
// exist, it is created before mounting.
func (f *Filesystem) Mount(path string) error {
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
return err
}
f.mountInfo.parentFd, err = os.OpenFile(path, syscall.O_ADMIN, os.ModePerm)
if err != nil {
return err
}
var rpcChan *zx.Channel
rpcChan, f.mountInfo.serveChannel, err = zx.NewChannel(0)
if err != nil {
f.mountInfo.parentFd.Close()
f.mountInfo.parentFd = nil
return fmt.Errorf("channel creation: %s", err)
}
if err := syscall.FDIOForFD(int(f.mountInfo.parentFd.Fd())).IoctlSetHandle(fdio.IoctlVFSMountFS, f.mountInfo.serveChannel.Handle); err != nil {
f.mountInfo.serveChannel.Close()
f.mountInfo.serveChannel = nil
f.mountInfo.parentFd.Close()
f.mountInfo.parentFd = nil
return fmt.Errorf("mount failure: %s", err)
}
vfs, err := rpc.NewServer(f, rpcChan.Handle)
if err != nil {
f.mountInfo.serveChannel.Close()
f.mountInfo.serveChannel = nil
f.mountInfo.parentFd.Close()
f.mountInfo.parentFd = nil
return fmt.Errorf("vfs server creation: %s", err)
}
// TODO(raggi): handle the exit case more cleanly.
go func() {
defer f.Unmount()
vfs.Serve()
}()
return nil
}
// Unmount detaches the filesystem from a previously mounted path. If mount was not previously called or successful, this will panic.
func (f *Filesystem) Unmount() {
f.mountInfo.unmountOnce.Do(func() {
// TODO(raggi): log errors?
syscall.FDIOForFD(int(f.mountInfo.parentFd.Fd())).Ioctl(fdio.IoctlVFSUnmountNode, nil, nil)
f.mountInfo.serveChannel.Close()
f.mountInfo.parentFd.Close()
f.mountInfo.serveChannel = nil
f.mountInfo.parentFd = nil
})
}
func goErrToFSErr(err error) error {
switch e := err.(type) {
case nil:
return nil
case *os.PathError:
return goErrToFSErr(e.Err)
case zx.Error:
switch e.Status {
case zx.ErrNotFound:
return fs.ErrNotFound
default:
debugLog("pkgfs: unmapped os err to fs err: %T %v", err, err)
return err
}
}
switch err {
case os.ErrInvalid:
return fs.ErrInvalidArgs
case os.ErrPermission:
return fs.ErrPermission
case os.ErrExist:
return fs.ErrAlreadyExists
case os.ErrNotExist:
return fs.ErrNotFound
case os.ErrClosed:
return fs.ErrNotOpen
case io.EOF:
return fs.ErrEOF
default:
debugLog("pkgfs: unmapped os err to fs err: %T %v", err, err)
return err
}
}