Packages

Filesystem in USErspace (FUSE) for Elixir

Current section

Files

Jump to
userfs lib userfs.ex
Raw

lib/userfs.ex

defmodule Userfs do
@moduledoc """
API calls to mount, and manage filesystems.
"""
defstruct mount_point: nil, fs_data: nil
@efuse_attr_dir :userfs_defs.attr_dir()
@efuse_attr_file :userfs_defs.attr_file()
@efuse_attr_symlink :userfs_defs.attr_symlink()
@type type :: unquote(@efuse_attr_dir) | unquote(@efuse_attr_file) | unquote(@efuse_attr_symlink)
@type mode :: unquote(0o755) | unquote(0o644)
@doc """
Mount a filesystem. The three parameters are the mount point, the callback
module which implements the filesystem, and a term, which can be anything, and
is passed to the filesystem implementation initialisation.
See `Userfs.Fs` for information on how to implement a filesystem callback
module.
iex> Userfs.mount("/tmp/my_elixir_fs", MyApp.Filesystem, my_fs_opts)
{:ok, #PID<0.194.0>}
Some example filesystems are provided which you can experiment with, and also
inspect for improved undestanding of how a filesystem is implemented.
"""
@spec mount(String.t, module, term) :: {:ok, pid}
def mount(mount_point, fs_mod, fs_state) do
Userfs.MountSup.start_child(mount_point, fs_mod, fs_state)
end
@doc """
Unmount a filesystem.
iex> Userfs.mount("/tmp/my_elixir_fs")
{:ok, {:stopping, #PID<0.194.0>}}
"""
@spec umount(String.t) :: {:ok, {:stopping, pid}} | {:error, :not_mounted}
def umount(mount_point) do
case Enum.filter(
list(),
fn({_pid, {this_mount_point, _fs_mod, _fs_state, _os_pid}}) ->
this_mount_point == mount_point
end
) do
[] ->
{:error, :not_mounted};
[{pid, _status}] ->
try do
:ok = Userfs.Server.stop(pid)
catch
:exit,{:noproc,_} -> :ok
:exit,:normal -> :ok
end
{:ok, pid}
end
end
@doc """
Enumerate the mounted filesystems. Returns a list of tuples, each
tuple having two elements, the first being the PID of the Elixir process
managing the FS, and the second being the status of the FS reported by it.
The status is a tuple with four elements, the mount point of the FS, the
callback module implementing the FS, the state of the FS (specific to the
implementation module) and the OS PID of the port process which links the
implementation module to the kernel of the OS.
iex> Userfs.list()
[{#PID<0.194.0>, {"/tmp/foo", Userfs.Fs.Hello, :ready, 29177}}]
"""
@spec list() :: [{pid, {String.t, atom, term, integer}}]
def list() do
Enum.filter(
Enum.map(
Userfs.MountSup.which_children(),
fn({:undefined, pid, :worker, [Userfs.Server]}) ->
status = try do
Userfs.Server.status(pid)
catch
:exit,{:noproc,_} -> :stopped
:exit,{:normal,_} -> :stopped
end
{pid, status}
end
),
fn({_pid, status}) -> status !== :stopped end
)
end
end