Tahr.NFSv3.Filehandle (tahr v0.1.0)

Copy Markdown View Source

Default opaque file-handle scheme for NFSv3 backends.

RFC 1813 §2.5 defines nfsfh3 as an opaque variable-length byte string up to 64 bytes. The handler treats handles as fully opaque — every interpretation (validation, lookup, refusal) lives in the backend. Most backends still need some packing, so this module provides one.

Layout


 tag (1 B)    vol_id       fileid (8 B)    generation   
              (16 B UUID)                  (4 B)        

                 29 bytes total  well under NFS3_FHSIZE = 64
  • tag0x01 for "v1 default scheme". Lets future schemes coexist without ambiguity.
  • vol_id — 16 raw bytes; meant for a UUID, but any 16-byte cluster-stable identifier works.
  • fileid — 64-bit big-endian fileid (any persistent inode number or equivalent identifier).
  • generation — 32-bit big-endian generation counter; bumped on delete-and-recreate so a stale handle returns NFS3ERR_STALE instead of pointing at a different file. Pass 0 if the backend doesn't track generations yet.

Wire encoding is just the 29-byte tuple — Tahr.NFSv3.Types.encode_fhandle3/1 wraps it in the variable-opaque XDR envelope.

Backends that want a different scheme

Skip this module entirely and pack whatever you like into the 64-byte budget. The Backend behaviour treats fhandle3() as binary().

Summary

Types

Reasons decode/1 may refuse with.

Decoded form of a v1 default-scheme handle.

Functions

Unpack a v1 default-scheme handle. Returns {:error, :badhandle} for any handle that isn't this scheme — including handles that are too short, too long, or use a different tag byte. Backends that pack with a different scheme should not call this function.

Pack {vol_id, fileid, generation} into a v1 default-scheme handle.

The fixed packed-handle byte size for this scheme.

Check whether a decoded handle's vol_id matches the expected volume. The handler doesn't enforce this — it's a backend helper for the common case of "refuse a handle that's for a different volume" with NFS3ERR_STALE.

The tag byte for v1 default-scheme handles.

Types

decode_error()

@type decode_error() :: :stale | :badhandle

Reasons decode/1 may refuse with.

decoded()

@type decoded() :: %{
  vol_id: <<_::128>>,
  fileid: non_neg_integer(),
  generation: non_neg_integer()
}

Decoded form of a v1 default-scheme handle.

Functions

decode(binary)

@spec decode(binary()) :: {:ok, decoded()} | {:error, decode_error()}

Unpack a v1 default-scheme handle. Returns {:error, :badhandle} for any handle that isn't this scheme — including handles that are too short, too long, or use a different tag byte. Backends that pack with a different scheme should not call this function.

encode(vol_id, fileid, generation \\ 0)

@spec encode(<<_::128>>, non_neg_integer(), non_neg_integer()) :: binary()

Pack {vol_id, fileid, generation} into a v1 default-scheme handle.

vol_id must be exactly 16 bytes. fileid and generation are 64-bit and 32-bit unsigned respectively. Raises ArgumentError on malformed input — every callsite passes already-validated cluster-internal values.

packed_size()

@spec packed_size() :: 29

The fixed packed-handle byte size for this scheme.

same_volume?(map, expected)

@spec same_volume?(decoded(), <<_::128>>) :: boolean()

Check whether a decoded handle's vol_id matches the expected volume. The handler doesn't enforce this — it's a backend helper for the common case of "refuse a handle that's for a different volume" with NFS3ERR_STALE.

tag()

@spec tag() :: byte()

The tag byte for v1 default-scheme handles.