We previously spoke about extended attributes
like they were just another piece of metadata attached to files.

However some have rather awkward interfaces as far as copying is concerned,
some because they don't depend on the file's contents itself,
and some because they are filesystem specific.

Selinux labels

Selinux is a complicated mandatory access control mechanism.

Rather than store the access control rules in the file,
like POSIX ACLs do,
the rules are stored elsewhere in the kernel
and a reference to what kind of file it is,
is stored in the file as an extended attribute
called the "security label".

The security label and the security context of the process accessing the file
are looked up in the ACL rules in the kernel
to determine whether the operation is permitted.

The details of how to define Selinux rules is complicated
and beyond the scope of this article.
We only care how we should reapply the rules when moving the file.

While we could copy the label from the old file into the new file,
as we did for POSIX ACLs,
Selinux contexts are defined by their file paths rather than the inodes,
so after we move a file we should relabel it
to what the file should have in the new location.

Using selinux_restorecon(3) might be tempting,
but it leaves open a race condition
where the file would be created with the wrong context
so it temporarily accessible with the wrong label.

If the file context should be preserved from the original file,
then you must read the context from the extended attribute,
either directly with fgetxattr(2) or fgetfilecon(3),
and then set the context before creating the new file
with setfscreatecon(3).

If instead it should have the label that the path database says it should be,
then the required context can be found by using selabel_open(3)
with SELABEL_CTX_FILE to get a reference to the file contexts database,
then getting the label it should have at that path using selabel_lookup(3),
and setting the context for new files with setfscreatecon(3).

The setfscreatecon(3) API is unfortunate as it involves global state.
Recent enough versions of Linux have the O_TMPFILE flag for file creation,
which doesn't create a directory entry for the file when it is created,
so you can modify the file before it is visible to other processes,
and can be bound into place with linkat(2).