Elixir Xattr v0.3.1 Xattr View Source

API module for accessing custom extended filesystem attributes.

Attributes managed by this module are stored in isolation, in custom namespace. Because implementation concepts of extended attributes differ in supported platforms, it would not be possible to provide unified API which could cover specific use cases.

Some kernels and filesystems may place various limits on extended attributes functionality, and so it is to use them only to store few, short metadata which is not crucial to application functionality.

Implementation

Elixir Xattr is implemented as NIF library with two platform-dependent backends:

  • Xattr - Unix extended attributes supported by Linux and macOS
  • Windows - alternate data streams available in Windows/NTFS

Xattr

This backed works as an Erlang wrapper for xattr(7) functionality available in Unix world. Attributes are always prefixed with user.ElixirXattr namespace.

Windows

On Windows, NTFS has a feature called Alternate Data Streams. Briefly: a file can have many contents.

Attributes are stored in ElixirXattr data stream, which is automatically created when setting an attribute and the stream does not exist. They are saved in simple binary format, as a contiguous list of size:data cells:

  v - name C-string size                          v - value binary size
+---+------------+---+-----------+---+----------+---+-------+
| 5 | n a m e \0 | 5 | v a l u e | 4 | f o o \0 | 3 | b a r |  ...
+---+------------+---+-----------+---+----------+---+-------+
      ^ - name C-string, note \0 suffix               ^ - value binary data

Unicode

Unicode filenames are supported (and as such proper encoding conversions are performed when needed).

Both names nor values are not processed and stored as-is.

Attribute name types

Because attribute names can be represented by various Erlang types, they are prefixed with type tags during serialization:

  • a$ - atoms
  • s$ - name

For example, given Xattr backend, call Xattr.set("foo.txt", "example", "value") will create user.ElixirXattr.s$example extended attribute on file foo.txt.

On both Unix and Windows implementations, attribute storage is attached to file system data, not file/link entries. Therefore attributes are shared between all hard links / file and its symlinks.

Errors

Because of the nature of error handling on both Unix and Windows, only specific error codes are translated to atoms. Other codes are stringified to some human readable name, on Unix using strerror and on Windows to form 'Windows Error {hexadecimal error code}' (Windows version of strerror returns localized messages on non-English installations).

Following errors are represented as atoms and as such can be pattern matched:

  • :enoattr - attribute was not found
  • :enotsup - extended attributes are not supported for this file
  • :enoent - file does not exist
  • :invalfmt - attribute storage is corrupted and should be regenerated

Link to this section Summary

Functions

Gets extended attribute value

The same as get/2, but raises an exception if it fails

Checks whether path has extended attribute name

The same as has/2, but raises an exception if it fails

Lists names of all extended attributes of path

The same as ls/1, but raises an exception if it fails

Removes extended attribute

The same as rm/2, but raises an exception if it fails

Sets extended attribute value

The same as set/3, but raises an exception if it fails

Link to this section Types

Link to this section Functions

Link to this function

get(path, name) View Source
get(Path.t(), name :: name_t()) :: {:ok, binary()} | {:error, term()}

Gets extended attribute value.

If attribute name does not exist, {:error, :enoattr} is returned.

Example

Xattr.set("foo.txt", "hello", "world")
Xattr.get("foo.txt", "hello") == {:ok, "world"}
Xattr.get("foo.txt", :foo) == {:error, :enoattr}
Link to this function

get!(path, name) View Source
get!(Path.t(), name :: name_t()) :: binary() | no_return()

The same as get/2, but raises an exception if it fails.

Link to this function

has(path, name) View Source
has(Path.t(), name :: name_t()) :: {:ok, boolean()} | {:error, term()}

Checks whether path has extended attribute name.

Example

Xattr.set("foo.txt", "hello", "world")
Xattr.has("foo.txt", "hello") == {:ok, true}
Xattr.has("foo.txt", :foo) == {:ok, false}
Link to this function

has!(path, name) View Source
has!(Path.t(), name :: name_t()) :: boolean() | no_return()

The same as has/2, but raises an exception if it fails.

Link to this function

ls(path) View Source
ls(Path.t()) :: {:ok, [name_t()]} | {:error, term()}

Lists names of all extended attributes of path.

The order of items in returned list is unspecified. If given path has no attributes, {:ok, []} is returned.

Example

Xattr.set("foo.txt", "hello", "world")
Xattr.set("foo.txt", :foo, "bar")
{:ok, list} = Xattr.ls("foo.txt")
# list should be permutation of ["hello", :foo]

The same as ls/1, but raises an exception if it fails.

Link to this function

rm(path, name) View Source
rm(Path.t(), name :: name_t()) :: :ok | {:error, term()}

Removes extended attribute.

If attribute name does not exist, {:error, :enoattr} is returned.

Example

Xattr.set("foo.txt", "hello", "world")
Xattr.set("foo.txt", :foo, "bar")
Xattr.rm("foo.txt", "foo")
{:ok, ["hello"]} = Xattr.ls("foo.txt")
Link to this function

rm!(path, name) View Source
rm!(Path.t(), name :: name_t()) :: :ok | no_return()

The same as rm/2, but raises an exception if it fails.

Link to this function

set(path, name, value) View Source
set(Path.t(), name :: name_t(), value :: binary()) :: :ok | {:error, term()}

Sets extended attribute value.

If attribute name does not exist, it is created.

Example

Xattr.set("foo.txt", "hello", "world")
Xattr.get("foo.txt", "hello") == {:ok, "world"}
Link to this function

set!(path, name, value) View Source
set!(Path.t(), name :: name_t(), value :: binary()) :: :ok | no_return()

The same as set/3, but raises an exception if it fails.