Exfuse.Fs behaviour (exfuse v0.1.0)

Copy Markdown View Source

Filesystems implement FUSE operations with route macros or handle_event/3. Exfuse.mount/3 mounts one filesystem process; routes define the directory tree below that mount point.

init do
  opts
end

readdir "/" do
  {:reply, ["docs"], socket}
end

getattr "/" do
  {:reply, dir(), socket}
end

readdir "/docs" do
  {:reply, Map.keys(state), socket}
end

getattr "/docs" do
  {:reply, dir(), socket}
end

getattr "/docs/:name" do
  {:reply, file(size: byte_size(state[name])), socket}
end

read "/docs/:name" do
  {:reply, read_chunk(state[name], event), socket}
end

plug "/media/*path", MyApp.MediaFile

:name binds one path segment as a binary. *name binds the remaining path tail as a list of segments, and bare * matches the remaining tail without binding it. Route blocks can read event, socket and state; params are endpoint variables, not fields on the socket. init blocks can read mount_point and opts.

Or implement handle_event/3 and pattern match on the event payload:

def handle_event(:getattr, %{path: "/"}, socket) do
  {:reply, dir(), socket}
end

def handle_event(:read, %{path: "/docs/" <> _} = event, socket) do
  {:reply, read_chunk(event), socket}
end

def handle_event(_op, _event, socket), do: {:error, :enoent, socket}

The payload is a map. Every payload includes path, uid, gid, pid, and umask. Extra fields:

  • :read - flags, handle, offset, size
  • :write - handle, offset, data
  • :open - flags
  • :create - mode, flags
  • :truncate - size
  • :rename - target
  • :mkdir and :chmod - mode
  • :chown - owner_uid, owner_gid
  • :flush and :release - flags, handle
  • :fsync - datasync, flags, handle

plug/2 delegates every operation matching the path to an endpoint process. Endpoint processes are keyed by route params. A plugged module can define init/1; after that it receives packets through handle_event/3.

defmodule MyApp.MediaFile do
  def init(socket) do
    {:ok, socket}
  end

  def handle_event(:read, %{params: %{file: file}} = event, socket) do
    {:reply, read_media(file, event), socket}
  end
end

Summary

Functions

Builds a directory attribute reply for getattr.

Builds a regular file attribute reply for getattr.

Builds a symlink attribute reply for getattr.

Types

event()

@type event() :: %{
  :path => String.t(),
  optional(:uid) => non_neg_integer(),
  optional(:gid) => non_neg_integer(),
  optional(:pid) => non_neg_integer(),
  optional(:umask) => non_neg_integer(),
  optional(:flags) => integer(),
  optional(:handle) => Exfuse.Socket.handle(),
  optional(:offset) => non_neg_integer(),
  optional(:size) => non_neg_integer(),
  optional(:mode) => non_neg_integer(),
  optional(:data) => binary(),
  optional(:target) => String.t(),
  optional(:owner_uid) => non_neg_integer(),
  optional(:owner_gid) => non_neg_integer(),
  optional(:datasync) => boolean()
}

event_result()

@type event_result() ::
  {:noreply, Exfuse.Socket.t()}
  | {:reply, term(), Exfuse.Socket.t()}
  | {:error, term(), Exfuse.Socket.t()}

operation()

@type operation() ::
  :readdir
  | :getattr
  | :readlink
  | :read
  | :write
  | :open
  | :create
  | :truncate
  | :unlink
  | :rename
  | :mkdir
  | :rmdir
  | :chmod
  | :chown
  | :flush
  | :release
  | :fsync

Callbacks

exfuse_init(t, term)

@callback exfuse_init(String.t(), term()) :: {:ok, term()} | {:error, term()}

handle_event(operation, event, t)

@callback handle_event(operation(), event(), Exfuse.Socket.t()) :: event_result()

Functions

dir(opts \\ [])

Builds a directory attribute reply for getattr.

file(opts \\ [])

Builds a regular file attribute reply for getattr.

symlink(opts \\ [])

Builds a symlink attribute reply for getattr.