4.7 The Origin Attribute

A merged path does not reveal which stratum provided it. stratafs exposes that through one synthetic extended attribute, system.stratafs.origin, and deliberately through nothing else — a tool that instead re-implemented §4.3's resolution rules against the strata would be a second implementation of them, and would eventually disagree with the first.

Together with the mount table (§4.2.2), which gives the stratum stack, it is enough to explain any path in a mount without privilege beyond what reading the path itself requires.

4.7.1 The value #

Reading the attribute returns the absolute path of the object that provides it:

  • For a non-directory, the provider's path in its stratum.
  • For a merged directory, the paths of every participating directory in precedence order, separated by newlines.

Each element is the stratum's own path, then a / where the relative part is non-empty and the base does not already end in one, then the relative path. Within a path, a newline or a backslash is escaped by a preceding backslash; those two are the whole escape set, and the / stratafs itself inserts is not escaped. Stratum paths are absolute because the mount parser required them to be (§4.2.2).

There is no trailing newline and no trailing NUL. A null buffer returns the length required; an undersized one returns ERANGE.

The value is synthesised at each read from the current resolution. It is not stored, and it is not the value of any attribute on any stratum. Because non-root dentries are always invalidated (§4.4.2), a read by path always reflects a fresh resolution.

4.7.2 Constraints #

The attribute is not settable. Any set or removal in the reserved namespace fails with EPERM, before the provider is reached at all.

It is not reported by an attribute listing. The listing handler filters reserved names out of both its sizing pass and its copy pass, and the synthetic name is never added to any listing. Hiding it keeps archivers, copy tools and backup software from discovering it, attempting to preserve it, and failing.

The whole system.stratafs. namespace is reserved. No read, write or removal of any name in it is forwarded to the provider: a read of any name other than origin returns ENODATA, and a write or removal returns EPERM. Where a provider object carries a real attribute of one of these names, it is masked — the synthesised value is returned instead, and the provider's attribute is absent from listings through the mount.

Two details of the implementation are worth stating exactly. The namespace test is a fixed-length prefix comparison against system.stratafs. including the trailing dot, so the bare name system.stratafs is not reserved and would be forwarded to the provider. And the reserved set is one name wider than the namespace suggests: the staging marker attribute, security.peios.stratafs_staging, receives the same treatment — EPERM to write, ENODATA to read, hidden from listings, masked from providers — despite lying outside the system.stratafs. namespace.

4.7.3 Access #

Reading the attribute requires the access that reading an extended attribute of the object requires, and is refused where that would be refused. The right to read the object's stat attributes is not sufficient: the request is for the read-EA right, which KACS's getxattr hook demands on the stratafs dentry before stratafs's own handler runs.

For a merged directory the value names every participating directory, so it discloses more than any one of them. Reading it requires that access on every participating directory and is refused where any refuses — the same intersection §4.6.2 applies to enumeration, and for the same reason: a caller who may not know a restricted directory participates must not learn it from this attribute.

Where the attribute is read through a directory descriptor, the participating set is the one settled when that descriptor was opened (§4.3.4), and the value names that set rather than the current one. The access decision was likewise made at open and is recorded on the dentry, so the read consults a stored verdict rather than re-checking. A stratum that has joined since is not disclosed, because the check that would have covered it was never run; a settled participant that has since ceased to hold the directory is still named, for the same reason.

Where it is read by path, the participating set is the current one, resolved afresh, and the read-EA right is required against each of its members at that moment.

Edit this page