1040 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
			
		
		
	
	
			1040 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
	
	
| .. SPDX-License-Identifier: GPL-2.0
 | |
| 
 | |
| Idmappings
 | |
| ==========
 | |
| 
 | |
| Most filesystem developers will have encountered idmappings. They are used when
 | |
| reading from or writing ownership to disk, reporting ownership to userspace, or
 | |
| for permission checking. This document is aimed at filesystem developers that
 | |
| want to know how idmappings work.
 | |
| 
 | |
| Formal notes
 | |
| ------------
 | |
| 
 | |
| An idmapping is essentially a translation of a range of ids into another or the
 | |
| same range of ids. The notational convention for idmappings that is widely used
 | |
| in userspace is::
 | |
| 
 | |
|  u:k:r
 | |
| 
 | |
| ``u`` indicates the first element in the upper idmapset ``U`` and ``k``
 | |
| indicates the first element in the lower idmapset ``K``. The ``r`` parameter
 | |
| indicates the range of the idmapping, i.e. how many ids are mapped. From now
 | |
| on, we will always prefix ids with ``u`` or ``k`` to make it clear whether
 | |
| we're talking about an id in the upper or lower idmapset.
 | |
| 
 | |
| To see what this looks like in practice, let's take the following idmapping::
 | |
| 
 | |
|  u22:k10000:r3
 | |
| 
 | |
| and write down the mappings it will generate::
 | |
| 
 | |
|  u22 -> k10000
 | |
|  u23 -> k10001
 | |
|  u24 -> k10002
 | |
| 
 | |
| From a mathematical viewpoint ``U`` and ``K`` are well-ordered sets and an
 | |
| idmapping is an order isomorphism from ``U`` into ``K``. So ``U`` and ``K`` are
 | |
| order isomorphic. In fact, ``U`` and ``K`` are always well-ordered subsets of
 | |
| the set of all possible ids usable on a given system.
 | |
| 
 | |
| Looking at this mathematically briefly will help us highlight some properties
 | |
| that make it easier to understand how we can translate between idmappings. For
 | |
| example, we know that the inverse idmapping is an order isomorphism as well::
 | |
| 
 | |
|  k10000 -> u22
 | |
|  k10001 -> u23
 | |
|  k10002 -> u24
 | |
| 
 | |
| Given that we are dealing with order isomorphisms plus the fact that we're
 | |
| dealing with subsets we can embed idmappings into each other, i.e. we can
 | |
| sensibly translate between different idmappings. For example, assume we've been
 | |
| given the three idmappings::
 | |
| 
 | |
|  1. u0:k10000:r10000
 | |
|  2. u0:k20000:r10000
 | |
|  3. u0:k30000:r10000
 | |
| 
 | |
| and id ``k11000`` which has been generated by the first idmapping by mapping
 | |
| ``u1000`` from the upper idmapset down to ``k11000`` in the lower idmapset.
 | |
| 
 | |
| Because we're dealing with order isomorphic subsets it is meaningful to ask
 | |
| what id ``k11000`` corresponds to in the second or third idmapping. The
 | |
| straightforward algorithm to use is to apply the inverse of the first idmapping,
 | |
| mapping ``k11000`` up to ``u1000``. Afterwards, we can map ``u1000`` down using
 | |
| either the second idmapping mapping or third idmapping mapping. The second
 | |
| idmapping would map ``u1000`` down to ``21000``. The third idmapping would map
 | |
| ``u1000`` down to ``u31000``.
 | |
| 
 | |
| If we were given the same task for the following three idmappings::
 | |
| 
 | |
|  1. u0:k10000:r10000
 | |
|  2. u0:k20000:r200
 | |
|  3. u0:k30000:r300
 | |
| 
 | |
| we would fail to translate as the sets aren't order isomorphic over the full
 | |
| range of the first idmapping anymore (However they are order isomorphic over
 | |
| the full range of the second idmapping.). Neither the second or third idmapping
 | |
| contain ``u1000`` in the upper idmapset ``U``. This is equivalent to not having
 | |
| an id mapped. We can simply say that ``u1000`` is unmapped in the second and
 | |
| third idmapping. The kernel will report unmapped ids as the overflowuid
 | |
| ``(uid_t)-1`` or overflowgid ``(gid_t)-1`` to userspace.
 | |
| 
 | |
| The algorithm to calculate what a given id maps to is pretty simple. First, we
 | |
| need to verify that the range can contain our target id. We will skip this step
 | |
| for simplicity. After that if we want to know what ``id`` maps to we can do
 | |
| simple calculations:
 | |
| 
 | |
| - If we want to map from left to right::
 | |
| 
 | |
|    u:k:r
 | |
|    id - u + k = n
 | |
| 
 | |
| - If we want to map from right to left::
 | |
| 
 | |
|    u:k:r
 | |
|    id - k + u = n
 | |
| 
 | |
| Instead of "left to right" we can also say "down" and instead of "right to
 | |
| left" we can also say "up". Obviously mapping down and up invert each other.
 | |
| 
 | |
| To see whether the simple formulas above work, consider the following two
 | |
| idmappings::
 | |
| 
 | |
|  1. u0:k20000:r10000
 | |
|  2. u500:k30000:r10000
 | |
| 
 | |
| Assume we are given ``k21000`` in the lower idmapset of the first idmapping. We
 | |
| want to know what id this was mapped from in the upper idmapset of the first
 | |
| idmapping. So we're mapping up in the first idmapping::
 | |
| 
 | |
|  id     - k      + u  = n
 | |
|  k21000 - k20000 + u0 = u1000
 | |
| 
 | |
| Now assume we are given the id ``u1100`` in the upper idmapset of the second
 | |
| idmapping and we want to know what this id maps down to in the lower idmapset
 | |
| of the second idmapping. This means we're mapping down in the second
 | |
| idmapping::
 | |
| 
 | |
|  id    - u    + k      = n
 | |
|  u1100 - u500 + k30000 = k30600
 | |
| 
 | |
| General notes
 | |
| -------------
 | |
| 
 | |
| In the context of the kernel an idmapping can be interpreted as mapping a range
 | |
| of userspace ids into a range of kernel ids::
 | |
| 
 | |
|  userspace-id:kernel-id:range
 | |
| 
 | |
| A userspace id is always an element in the upper idmapset of an idmapping of
 | |
| type ``uid_t`` or ``gid_t`` and a kernel id is always an element in the lower
 | |
| idmapset of an idmapping of type ``kuid_t`` or ``kgid_t``. From now on
 | |
| "userspace id" will be used to refer to the well known ``uid_t`` and ``gid_t``
 | |
| types and "kernel id" will be used to refer to ``kuid_t`` and ``kgid_t``.
 | |
| 
 | |
| The kernel is mostly concerned with kernel ids. They are used when performing
 | |
| permission checks and are stored in an inode's ``i_uid`` and ``i_gid`` field.
 | |
| A userspace id on the other hand is an id that is reported to userspace by the
 | |
| kernel, or is passed by userspace to the kernel, or a raw device id that is
 | |
| written or read from disk.
 | |
| 
 | |
| Note that we are only concerned with idmappings as the kernel stores them not
 | |
| how userspace would specify them.
 | |
| 
 | |
| For the rest of this document we will prefix all userspace ids with ``u`` and
 | |
| all kernel ids with ``k``. Ranges of idmappings will be prefixed with ``r``. So
 | |
| an idmapping will be written as ``u0:k10000:r10000``.
 | |
| 
 | |
| For example, within this idmapping, the id ``u1000`` is an id in the upper
 | |
| idmapset or "userspace idmapset" starting with ``u0``. And it is mapped to
 | |
| ``k11000`` which is a kernel id in the lower idmapset or "kernel idmapset"
 | |
| starting with ``k10000``.
 | |
| 
 | |
| A kernel id is always created by an idmapping. Such idmappings are associated
 | |
| with user namespaces. Since we mainly care about how idmappings work we're not
 | |
| going to be concerned with how idmappings are created nor how they are used
 | |
| outside of the filesystem context. This is best left to an explanation of user
 | |
| namespaces.
 | |
| 
 | |
| The initial user namespace is special. It always has an idmapping of the
 | |
| following form::
 | |
| 
 | |
|  u0:k0:r4294967295
 | |
| 
 | |
| which is an identity idmapping over the full range of ids available on this
 | |
| system.
 | |
| 
 | |
| Other user namespaces usually have non-identity idmappings such as::
 | |
| 
 | |
|  u0:k10000:r10000
 | |
| 
 | |
| When a process creates or wants to change ownership of a file, or when the
 | |
| ownership of a file is read from disk by a filesystem, the userspace id is
 | |
| immediately translated into a kernel id according to the idmapping associated
 | |
| with the relevant user namespace.
 | |
| 
 | |
| For instance, consider a file that is stored on disk by a filesystem as being
 | |
| owned by ``u1000``:
 | |
| 
 | |
| - If a filesystem were to be mounted in the initial user namespaces (as most
 | |
|   filesystems are) then the initial idmapping will be used. As we saw this is
 | |
|   simply the identity idmapping. This would mean id ``u1000`` read from disk
 | |
|   would be mapped to id ``k1000``. So an inode's ``i_uid`` and ``i_gid`` field
 | |
|   would contain ``k1000``.
 | |
| 
 | |
| - If a filesystem were to be mounted with an idmapping of ``u0:k10000:r10000``
 | |
|   then ``u1000`` read from disk would be mapped to ``k11000``. So an inode's
 | |
|   ``i_uid`` and ``i_gid`` would contain ``k11000``.
 | |
| 
 | |
| Translation algorithms
 | |
| ----------------------
 | |
| 
 | |
| We've already seen briefly that it is possible to translate between different
 | |
| idmappings. We'll now take a closer look how that works.
 | |
| 
 | |
| Crossmapping
 | |
| ~~~~~~~~~~~~
 | |
| 
 | |
| This translation algorithm is used by the kernel in quite a few places. For
 | |
| example, it is used when reporting back the ownership of a file to userspace
 | |
| via the ``stat()`` system call family.
 | |
| 
 | |
| If we've been given ``k11000`` from one idmapping we can map that id up in
 | |
| another idmapping. In order for this to work both idmappings need to contain
 | |
| the same kernel id in their kernel idmapsets. For example, consider the
 | |
| following idmappings::
 | |
| 
 | |
|  1. u0:k10000:r10000
 | |
|  2. u20000:k10000:r10000
 | |
| 
 | |
| and we are mapping ``u1000`` down to ``k11000`` in the first idmapping . We can
 | |
| then translate ``k11000`` into a userspace id in the second idmapping using the
 | |
| kernel idmapset of the second idmapping::
 | |
| 
 | |
|  /* Map the kernel id up into a userspace id in the second idmapping. */
 | |
|  from_kuid(u20000:k10000:r10000, k11000) = u21000
 | |
| 
 | |
| Note, how we can get back to the kernel id in the first idmapping by inverting
 | |
| the algorithm::
 | |
| 
 | |
|  /* Map the userspace id down into a kernel id in the second idmapping. */
 | |
|  make_kuid(u20000:k10000:r10000, u21000) = k11000
 | |
| 
 | |
|  /* Map the kernel id up into a userspace id in the first idmapping. */
 | |
|  from_kuid(u0:k10000:r10000, k11000) = u1000
 | |
| 
 | |
| This algorithm allows us to answer the question what userspace id a given
 | |
| kernel id corresponds to in a given idmapping. In order to be able to answer
 | |
| this question both idmappings need to contain the same kernel id in their
 | |
| respective kernel idmapsets.
 | |
| 
 | |
| For example, when the kernel reads a raw userspace id from disk it maps it down
 | |
| into a kernel id according to the idmapping associated with the filesystem.
 | |
| Let's assume the filesystem was mounted with an idmapping of
 | |
| ``u0:k20000:r10000`` and it reads a file owned by ``u1000`` from disk. This
 | |
| means ``u1000`` will be mapped to ``k21000`` which is what will be stored in
 | |
| the inode's ``i_uid`` and ``i_gid`` field.
 | |
| 
 | |
| When someone in userspace calls ``stat()`` or a related function to get
 | |
| ownership information about the file the kernel can't simply map the id back up
 | |
| according to the filesystem's idmapping as this would give the wrong owner if
 | |
| the caller is using an idmapping.
 | |
| 
 | |
| So the kernel will map the id back up in the idmapping of the caller. Let's
 | |
| assume the caller has the somewhat unconventional idmapping
 | |
| ``u3000:k20000:r10000`` then ``k21000`` would map back up to ``u4000``.
 | |
| Consequently the user would see that this file is owned by ``u4000``.
 | |
| 
 | |
| Remapping
 | |
| ~~~~~~~~~
 | |
| 
 | |
| It is possible to translate a kernel id from one idmapping to another one via
 | |
| the userspace idmapset of the two idmappings. This is equivalent to remapping
 | |
| a kernel id.
 | |
| 
 | |
| Let's look at an example. We are given the following two idmappings::
 | |
| 
 | |
|  1. u0:k10000:r10000
 | |
|  2. u0:k20000:r10000
 | |
| 
 | |
| and we are given ``k11000`` in the first idmapping. In order to translate this
 | |
| kernel id in the first idmapping into a kernel id in the second idmapping we
 | |
| need to perform two steps:
 | |
| 
 | |
| 1. Map the kernel id up into a userspace id in the first idmapping::
 | |
| 
 | |
|     /* Map the kernel id up into a userspace id in the first idmapping. */
 | |
|     from_kuid(u0:k10000:r10000, k11000) = u1000
 | |
| 
 | |
| 2. Map the userspace id down into a kernel id in the second idmapping::
 | |
| 
 | |
|     /* Map the userspace id down into a kernel id in the second idmapping. */
 | |
|     make_kuid(u0:k20000:r10000, u1000) = k21000
 | |
| 
 | |
| As you can see we used the userspace idmapset in both idmappings to translate
 | |
| the kernel id in one idmapping to a kernel id in another idmapping.
 | |
| 
 | |
| This allows us to answer the question what kernel id we would need to use to
 | |
| get the same userspace id in another idmapping. In order to be able to answer
 | |
| this question both idmappings need to contain the same userspace id in their
 | |
| respective userspace idmapsets.
 | |
| 
 | |
| Note, how we can easily get back to the kernel id in the first idmapping by
 | |
| inverting the algorithm:
 | |
| 
 | |
| 1. Map the kernel id up into a userspace id in the second idmapping::
 | |
| 
 | |
|     /* Map the kernel id up into a userspace id in the second idmapping. */
 | |
|     from_kuid(u0:k20000:r10000, k21000) = u1000
 | |
| 
 | |
| 2. Map the userspace id down into a kernel id in the first idmapping::
 | |
| 
 | |
|     /* Map the userspace id down into a kernel id in the first idmapping. */
 | |
|     make_kuid(u0:k10000:r10000, u1000) = k11000
 | |
| 
 | |
| Another way to look at this translation is to treat it as inverting one
 | |
| idmapping and applying another idmapping if both idmappings have the relevant
 | |
| userspace id mapped. This will come in handy when working with idmapped mounts.
 | |
| 
 | |
| Invalid translations
 | |
| ~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| It is never valid to use an id in the kernel idmapset of one idmapping as the
 | |
| id in the userspace idmapset of another or the same idmapping. While the kernel
 | |
| idmapset always indicates an idmapset in the kernel id space the userspace
 | |
| idmapset indicates a userspace id. So the following translations are forbidden::
 | |
| 
 | |
|  /* Map the userspace id down into a kernel id in the first idmapping. */
 | |
|  make_kuid(u0:k10000:r10000, u1000) = k11000
 | |
| 
 | |
|  /* INVALID: Map the kernel id down into a kernel id in the second idmapping. */
 | |
|  make_kuid(u10000:k20000:r10000, k110000) = k21000
 | |
|                                  ~~~~~~~
 | |
| 
 | |
| and equally wrong::
 | |
| 
 | |
|  /* Map the kernel id up into a userspace id in the first idmapping. */
 | |
|  from_kuid(u0:k10000:r10000, k11000) = u1000
 | |
| 
 | |
|  /* INVALID: Map the userspace id up into a userspace id in the second idmapping. */
 | |
|  from_kuid(u20000:k0:r10000, u1000) = k21000
 | |
|                              ~~~~~
 | |
| 
 | |
| Since userspace ids have type ``uid_t`` and ``gid_t`` and kernel ids have type
 | |
| ``kuid_t`` and ``kgid_t`` the compiler will throw an error when they are
 | |
| conflated. So the two examples above would cause a compilation failure.
 | |
| 
 | |
| Idmappings when creating filesystem objects
 | |
| -------------------------------------------
 | |
| 
 | |
| The concepts of mapping an id down or mapping an id up are expressed in the two
 | |
| kernel functions filesystem developers are rather familiar with and which we've
 | |
| already used in this document::
 | |
| 
 | |
|  /* Map the userspace id down into a kernel id. */
 | |
|  make_kuid(idmapping, uid)
 | |
| 
 | |
|  /* Map the kernel id up into a userspace id. */
 | |
|  from_kuid(idmapping, kuid)
 | |
| 
 | |
| We will take an abbreviated look into how idmappings figure into creating
 | |
| filesystem objects. For simplicity we will only look at what happens when the
 | |
| VFS has already completed path lookup right before it calls into the filesystem
 | |
| itself. So we're concerned with what happens when e.g. ``vfs_mkdir()`` is
 | |
| called. We will also assume that the directory we're creating filesystem
 | |
| objects in is readable and writable for everyone.
 | |
| 
 | |
| When creating a filesystem object the caller will look at the caller's
 | |
| filesystem ids. These are just regular ``uid_t`` and ``gid_t`` userspace ids
 | |
| but they are exclusively used when determining file ownership which is why they
 | |
| are called "filesystem ids". They are usually identical to the uid and gid of
 | |
| the caller but can differ. We will just assume they are always identical to not
 | |
| get lost in too many details.
 | |
| 
 | |
| When the caller enters the kernel two things happen:
 | |
| 
 | |
| 1. Map the caller's userspace ids down into kernel ids in the caller's
 | |
|    idmapping.
 | |
|    (To be precise, the kernel will simply look at the kernel ids stashed in the
 | |
|    credentials of the current task but for our education we'll pretend this
 | |
|    translation happens just in time.)
 | |
| 2. Verify that the caller's kernel ids can be mapped up to userspace ids in the
 | |
|    filesystem's idmapping.
 | |
| 
 | |
| The second step is important as regular filesystem will ultimately need to map
 | |
| the kernel id back up into a userspace id when writing to disk.
 | |
| So with the second step the kernel guarantees that a valid userspace id can be
 | |
| written to disk. If it can't the kernel will refuse the creation request to not
 | |
| even remotely risk filesystem corruption.
 | |
| 
 | |
| The astute reader will have realized that this is simply a variation of the
 | |
| crossmapping algorithm we mentioned above in a previous section. First, the
 | |
| kernel maps the caller's userspace id down into a kernel id according to the
 | |
| caller's idmapping and then maps that kernel id up according to the
 | |
| filesystem's idmapping.
 | |
| 
 | |
| From the implementation point it's worth mentioning how idmappings are represented.
 | |
| All idmappings are taken from the corresponding user namespace.
 | |
| 
 | |
|     - caller's idmapping (usually taken from ``current_user_ns()``)
 | |
|     - filesystem's idmapping (``sb->s_user_ns``)
 | |
|     - mount's idmapping (``mnt_idmap(vfsmnt)``)
 | |
| 
 | |
| Let's see some examples with caller/filesystem idmapping but without mount
 | |
| idmappings. This will exhibit some problems we can hit. After that we will
 | |
| revisit/reconsider these examples, this time using mount idmappings, to see how
 | |
| they can solve the problems we observed before.
 | |
| 
 | |
| Example 1
 | |
| ~~~~~~~~~
 | |
| 
 | |
| ::
 | |
| 
 | |
|  caller id:            u1000
 | |
|  caller idmapping:     u0:k0:r4294967295
 | |
|  filesystem idmapping: u0:k0:r4294967295
 | |
| 
 | |
| Both the caller and the filesystem use the identity idmapping:
 | |
| 
 | |
| 1. Map the caller's userspace ids into kernel ids in the caller's idmapping::
 | |
| 
 | |
|     make_kuid(u0:k0:r4294967295, u1000) = k1000
 | |
| 
 | |
| 2. Verify that the caller's kernel ids can be mapped to userspace ids in the
 | |
|    filesystem's idmapping.
 | |
| 
 | |
|    For this second step the kernel will call the function
 | |
|    ``fsuidgid_has_mapping()`` which ultimately boils down to calling
 | |
|    ``from_kuid()``::
 | |
| 
 | |
|     from_kuid(u0:k0:r4294967295, k1000) = u1000
 | |
| 
 | |
| In this example both idmappings are the same so there's nothing exciting going
 | |
| on. Ultimately the userspace id that lands on disk will be ``u1000``.
 | |
| 
 | |
| Example 2
 | |
| ~~~~~~~~~
 | |
| 
 | |
| ::
 | |
| 
 | |
|  caller id:            u1000
 | |
|  caller idmapping:     u0:k10000:r10000
 | |
|  filesystem idmapping: u0:k20000:r10000
 | |
| 
 | |
| 1. Map the caller's userspace ids down into kernel ids in the caller's
 | |
|    idmapping::
 | |
| 
 | |
|     make_kuid(u0:k10000:r10000, u1000) = k11000
 | |
| 
 | |
| 2. Verify that the caller's kernel ids can be mapped up to userspace ids in the
 | |
|    filesystem's idmapping::
 | |
| 
 | |
|     from_kuid(u0:k20000:r10000, k11000) = u-1
 | |
| 
 | |
| It's immediately clear that while the caller's userspace id could be
 | |
| successfully mapped down into kernel ids in the caller's idmapping the kernel
 | |
| ids could not be mapped up according to the filesystem's idmapping. So the
 | |
| kernel will deny this creation request.
 | |
| 
 | |
| Note that while this example is less common, because most filesystem can't be
 | |
| mounted with non-initial idmappings this is a general problem as we can see in
 | |
| the next examples.
 | |
| 
 | |
| Example 3
 | |
| ~~~~~~~~~
 | |
| 
 | |
| ::
 | |
| 
 | |
|  caller id:            u1000
 | |
|  caller idmapping:     u0:k10000:r10000
 | |
|  filesystem idmapping: u0:k0:r4294967295
 | |
| 
 | |
| 1. Map the caller's userspace ids down into kernel ids in the caller's
 | |
|    idmapping::
 | |
| 
 | |
|     make_kuid(u0:k10000:r10000, u1000) = k11000
 | |
| 
 | |
| 2. Verify that the caller's kernel ids can be mapped up to userspace ids in the
 | |
|    filesystem's idmapping::
 | |
| 
 | |
|     from_kuid(u0:k0:r4294967295, k11000) = u11000
 | |
| 
 | |
| We can see that the translation always succeeds. The userspace id that the
 | |
| filesystem will ultimately put to disk will always be identical to the value of
 | |
| the kernel id that was created in the caller's idmapping. This has mainly two
 | |
| consequences.
 | |
| 
 | |
| First, that we can't allow a caller to ultimately write to disk with another
 | |
| userspace id. We could only do this if we were to mount the whole filesystem
 | |
| with the caller's or another idmapping. But that solution is limited to a few
 | |
| filesystems and not very flexible. But this is a use-case that is pretty
 | |
| important in containerized workloads.
 | |
| 
 | |
| Second, the caller will usually not be able to create any files or access
 | |
| directories that have stricter permissions because none of the filesystem's
 | |
| kernel ids map up into valid userspace ids in the caller's idmapping
 | |
| 
 | |
| 1. Map raw userspace ids down to kernel ids in the filesystem's idmapping::
 | |
| 
 | |
|     make_kuid(u0:k0:r4294967295, u1000) = k1000
 | |
| 
 | |
| 2. Map kernel ids up to userspace ids in the caller's idmapping::
 | |
| 
 | |
|     from_kuid(u0:k10000:r10000, k1000) = u-1
 | |
| 
 | |
| Example 4
 | |
| ~~~~~~~~~
 | |
| 
 | |
| ::
 | |
| 
 | |
|  file id:              u1000
 | |
|  caller idmapping:     u0:k10000:r10000
 | |
|  filesystem idmapping: u0:k0:r4294967295
 | |
| 
 | |
| In order to report ownership to userspace the kernel uses the crossmapping
 | |
| algorithm introduced in a previous section:
 | |
| 
 | |
| 1. Map the userspace id on disk down into a kernel id in the filesystem's
 | |
|    idmapping::
 | |
| 
 | |
|     make_kuid(u0:k0:r4294967295, u1000) = k1000
 | |
| 
 | |
| 2. Map the kernel id up into a userspace id in the caller's idmapping::
 | |
| 
 | |
|     from_kuid(u0:k10000:r10000, k1000) = u-1
 | |
| 
 | |
| The crossmapping algorithm fails in this case because the kernel id in the
 | |
| filesystem idmapping cannot be mapped up to a userspace id in the caller's
 | |
| idmapping. Thus, the kernel will report the ownership of this file as the
 | |
| overflowid.
 | |
| 
 | |
| Example 5
 | |
| ~~~~~~~~~
 | |
| 
 | |
| ::
 | |
| 
 | |
|  file id:              u1000
 | |
|  caller idmapping:     u0:k10000:r10000
 | |
|  filesystem idmapping: u0:k20000:r10000
 | |
| 
 | |
| In order to report ownership to userspace the kernel uses the crossmapping
 | |
| algorithm introduced in a previous section:
 | |
| 
 | |
| 1. Map the userspace id on disk down into a kernel id in the filesystem's
 | |
|    idmapping::
 | |
| 
 | |
|     make_kuid(u0:k20000:r10000, u1000) = k21000
 | |
| 
 | |
| 2. Map the kernel id up into a userspace id in the caller's idmapping::
 | |
| 
 | |
|     from_kuid(u0:k10000:r10000, k21000) = u-1
 | |
| 
 | |
| Again, the crossmapping algorithm fails in this case because the kernel id in
 | |
| the filesystem idmapping cannot be mapped to a userspace id in the caller's
 | |
| idmapping. Thus, the kernel will report the ownership of this file as the
 | |
| overflowid.
 | |
| 
 | |
| Note how in the last two examples things would be simple if the caller would be
 | |
| using the initial idmapping. For a filesystem mounted with the initial
 | |
| idmapping it would be trivial. So we only consider a filesystem with an
 | |
| idmapping of ``u0:k20000:r10000``:
 | |
| 
 | |
| 1. Map the userspace id on disk down into a kernel id in the filesystem's
 | |
|    idmapping::
 | |
| 
 | |
|     make_kuid(u0:k20000:r10000, u1000) = k21000
 | |
| 
 | |
| 2. Map the kernel id up into a userspace id in the caller's idmapping::
 | |
| 
 | |
|     from_kuid(u0:k0:r4294967295, k21000) = u21000
 | |
| 
 | |
| Idmappings on idmapped mounts
 | |
| -----------------------------
 | |
| 
 | |
| The examples we've seen in the previous section where the caller's idmapping
 | |
| and the filesystem's idmapping are incompatible causes various issues for
 | |
| workloads. For a more complex but common example, consider two containers
 | |
| started on the host. To completely prevent the two containers from affecting
 | |
| each other, an administrator may often use different non-overlapping idmappings
 | |
| for the two containers::
 | |
| 
 | |
|  container1 idmapping:  u0:k10000:r10000
 | |
|  container2 idmapping:  u0:k20000:r10000
 | |
|  filesystem idmapping:  u0:k30000:r10000
 | |
| 
 | |
| An administrator wanting to provide easy read-write access to the following set
 | |
| of files::
 | |
| 
 | |
|  dir id:       u0
 | |
|  dir/file1 id: u1000
 | |
|  dir/file2 id: u2000
 | |
| 
 | |
| to both containers currently can't.
 | |
| 
 | |
| Of course the administrator has the option to recursively change ownership via
 | |
| ``chown()``. For example, they could change ownership so that ``dir`` and all
 | |
| files below it can be crossmapped from the filesystem's into the container's
 | |
| idmapping. Let's assume they change ownership so it is compatible with the
 | |
| first container's idmapping::
 | |
| 
 | |
|  dir id:       u10000
 | |
|  dir/file1 id: u11000
 | |
|  dir/file2 id: u12000
 | |
| 
 | |
| This would still leave ``dir`` rather useless to the second container. In fact,
 | |
| ``dir`` and all files below it would continue to appear owned by the overflowid
 | |
| for the second container.
 | |
| 
 | |
| Or consider another increasingly popular example. Some service managers such as
 | |
| systemd implement a concept called "portable home directories". A user may want
 | |
| to use their home directories on different machines where they are assigned
 | |
| different login userspace ids. Most users will have ``u1000`` as the login id
 | |
| on their machine at home and all files in their home directory will usually be
 | |
| owned by ``u1000``. At uni or at work they may have another login id such as
 | |
| ``u1125``. This makes it rather difficult to interact with their home directory
 | |
| on their work machine.
 | |
| 
 | |
| In both cases changing ownership recursively has grave implications. The most
 | |
| obvious one is that ownership is changed globally and permanently. In the home
 | |
| directory case this change in ownership would even need to happen every time the
 | |
| user switches from their home to their work machine. For really large sets of
 | |
| files this becomes increasingly costly.
 | |
| 
 | |
| If the user is lucky, they are dealing with a filesystem that is mountable
 | |
| inside user namespaces. But this would also change ownership globally and the
 | |
| change in ownership is tied to the lifetime of the filesystem mount, i.e. the
 | |
| superblock. The only way to change ownership is to completely unmount the
 | |
| filesystem and mount it again in another user namespace. This is usually
 | |
| impossible because it would mean that all users currently accessing the
 | |
| filesystem can't anymore. And it means that ``dir`` still can't be shared
 | |
| between two containers with different idmappings.
 | |
| But usually the user doesn't even have this option since most filesystems
 | |
| aren't mountable inside containers. And not having them mountable might be
 | |
| desirable as it doesn't require the filesystem to deal with malicious
 | |
| filesystem images.
 | |
| 
 | |
| But the usecases mentioned above and more can be handled by idmapped mounts.
 | |
| They allow to expose the same set of dentries with different ownership at
 | |
| different mounts. This is achieved by marking the mounts with a user namespace
 | |
| through the ``mount_setattr()`` system call. The idmapping associated with it
 | |
| is then used to translate from the caller's idmapping to the filesystem's
 | |
| idmapping and vica versa using the remapping algorithm we introduced above.
 | |
| 
 | |
| Idmapped mounts make it possible to change ownership in a temporary and
 | |
| localized way. The ownership changes are restricted to a specific mount and the
 | |
| ownership changes are tied to the lifetime of the mount. All other users and
 | |
| locations where the filesystem is exposed are unaffected.
 | |
| 
 | |
| Filesystems that support idmapped mounts don't have any real reason to support
 | |
| being mountable inside user namespaces. A filesystem could be exposed
 | |
| completely under an idmapped mount to get the same effect. This has the
 | |
| advantage that filesystems can leave the creation of the superblock to
 | |
| privileged users in the initial user namespace.
 | |
| 
 | |
| However, it is perfectly possible to combine idmapped mounts with filesystems
 | |
| mountable inside user namespaces. We will touch on this further below.
 | |
| 
 | |
| Filesystem types vs idmapped mount types
 | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| With the introduction of idmapped mounts we need to distinguish between
 | |
| filesystem ownership and mount ownership of a VFS object such as an inode. The
 | |
| owner of a inode might be different when looked at from a filesystem
 | |
| perspective than when looked at from an idmapped mount. Such fundamental
 | |
| conceptual distinctions should almost always be clearly expressed in the code.
 | |
| So, to distinguish idmapped mount ownership from filesystem ownership separate
 | |
| types have been introduced.
 | |
| 
 | |
| If a uid or gid has been generated using the filesystem or caller's idmapping
 | |
| then we will use the ``kuid_t`` and ``kgid_t`` types. However, if a uid or gid
 | |
| has been generated using a mount idmapping then we will be using the dedicated
 | |
| ``vfsuid_t`` and ``vfsgid_t`` types.
 | |
| 
 | |
| All VFS helpers that generate or take uids and gids as arguments use the
 | |
| ``vfsuid_t`` and ``vfsgid_t`` types and we will be able to rely on the compiler
 | |
| to catch errors that originate from conflating filesystem and VFS uids and gids.
 | |
| 
 | |
| The ``vfsuid_t`` and ``vfsgid_t`` types are often mapped from and to ``kuid_t``
 | |
| and ``kgid_t`` types similar how ``kuid_t`` and ``kgid_t`` types are mapped
 | |
| from and to ``uid_t`` and ``gid_t`` types::
 | |
| 
 | |
|  uid_t <--> kuid_t <--> vfsuid_t
 | |
|  gid_t <--> kgid_t <--> vfsgid_t
 | |
| 
 | |
| Whenever we report ownership based on a ``vfsuid_t`` or ``vfsgid_t`` type,
 | |
| e.g., during ``stat()``, or store ownership information in a shared VFS object
 | |
| based on a ``vfsuid_t`` or ``vfsgid_t`` type, e.g., during ``chown()`` we can
 | |
| use the ``vfsuid_into_kuid()`` and ``vfsgid_into_kgid()`` helpers.
 | |
| 
 | |
| To illustrate why this helper currently exists, consider what happens when we
 | |
| change ownership of an inode from an idmapped mount. After we generated
 | |
| a ``vfsuid_t`` or ``vfsgid_t`` based on the mount idmapping we later commit to
 | |
| this ``vfsuid_t`` or ``vfsgid_t`` to become the new filesystem wide ownership.
 | |
| Thus, we are turning the ``vfsuid_t`` or ``vfsgid_t`` into a global ``kuid_t``
 | |
| or ``kgid_t``. And this can be done by using ``vfsuid_into_kuid()`` and
 | |
| ``vfsgid_into_kgid()``.
 | |
| 
 | |
| Note, whenever a shared VFS object, e.g., a cached ``struct inode`` or a cached
 | |
| ``struct posix_acl``, stores ownership information a filesystem or "global"
 | |
| ``kuid_t`` and ``kgid_t`` must be used. Ownership expressed via ``vfsuid_t``
 | |
| and ``vfsgid_t`` is specific to an idmapped mount.
 | |
| 
 | |
| We already noted that ``vfsuid_t`` and ``vfsgid_t`` types are generated based
 | |
| on mount idmappings whereas ``kuid_t`` and ``kgid_t`` types are generated based
 | |
| on filesystem idmappings. To prevent abusing filesystem idmappings to generate
 | |
| ``vfsuid_t`` or ``vfsgid_t`` types or mount idmappings to generate ``kuid_t``
 | |
| or ``kgid_t`` types filesystem idmappings and mount idmappings are different
 | |
| types as well.
 | |
| 
 | |
| All helpers that map to or from ``vfsuid_t`` and ``vfsgid_t`` types require
 | |
| a mount idmapping to be passed which is of type ``struct mnt_idmap``. Passing
 | |
| a filesystem or caller idmapping will cause a compilation error.
 | |
| 
 | |
| Similar to how we prefix all userspace ids in this document with ``u`` and all
 | |
| kernel ids with ``k`` we will prefix all VFS ids with ``v``. So a mount
 | |
| idmapping will be written as: ``u0:v10000:r10000``.
 | |
| 
 | |
| Remapping helpers
 | |
| ~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| Idmapping functions were added that translate between idmappings. They make use
 | |
| of the remapping algorithm we've introduced earlier. We're going to look at:
 | |
| 
 | |
| - ``i_uid_into_vfsuid()`` and ``i_gid_into_vfsgid()``
 | |
| 
 | |
|   The ``i_*id_into_vfs*id()`` functions translate filesystem's kernel ids into
 | |
|   VFS ids in the mount's idmapping::
 | |
| 
 | |
|    /* Map the filesystem's kernel id up into a userspace id in the filesystem's idmapping. */
 | |
|    from_kuid(filesystem, kid) = uid
 | |
| 
 | |
|    /* Map the filesystem's userspace id down ito a VFS id in the mount's idmapping. */
 | |
|    make_kuid(mount, uid) = kuid
 | |
| 
 | |
| - ``mapped_fsuid()`` and ``mapped_fsgid()``
 | |
| 
 | |
|   The ``mapped_fs*id()`` functions translate the caller's kernel ids into
 | |
|   kernel ids in the filesystem's idmapping. This translation is achieved by
 | |
|   remapping the caller's VFS ids using the mount's idmapping::
 | |
| 
 | |
|    /* Map the caller's VFS id up into a userspace id in the mount's idmapping. */
 | |
|    from_kuid(mount, kid) = uid
 | |
| 
 | |
|    /* Map the mount's userspace id down into a kernel id in the filesystem's idmapping. */
 | |
|    make_kuid(filesystem, uid) = kuid
 | |
| 
 | |
| - ``vfsuid_into_kuid()`` and ``vfsgid_into_kgid()``
 | |
| 
 | |
|    Whenever
 | |
| 
 | |
| Note that these two functions invert each other. Consider the following
 | |
| idmappings::
 | |
| 
 | |
|  caller idmapping:     u0:k10000:r10000
 | |
|  filesystem idmapping: u0:k20000:r10000
 | |
|  mount idmapping:      u0:v10000:r10000
 | |
| 
 | |
| Assume a file owned by ``u1000`` is read from disk. The filesystem maps this id
 | |
| to ``k21000`` according to its idmapping. This is what is stored in the
 | |
| inode's ``i_uid`` and ``i_gid`` fields.
 | |
| 
 | |
| When the caller queries the ownership of this file via ``stat()`` the kernel
 | |
| would usually simply use the crossmapping algorithm and map the filesystem's
 | |
| kernel id up to a userspace id in the caller's idmapping.
 | |
| 
 | |
| But when the caller is accessing the file on an idmapped mount the kernel will
 | |
| first call ``i_uid_into_vfsuid()`` thereby translating the filesystem's kernel
 | |
| id into a VFS id in the mount's idmapping::
 | |
| 
 | |
|  i_uid_into_vfsuid(k21000):
 | |
|    /* Map the filesystem's kernel id up into a userspace id. */
 | |
|    from_kuid(u0:k20000:r10000, k21000) = u1000
 | |
| 
 | |
|    /* Map the filesystem's userspace id down into a VFS id in the mount's idmapping. */
 | |
|    make_kuid(u0:v10000:r10000, u1000) = v11000
 | |
| 
 | |
| Finally, when the kernel reports the owner to the caller it will turn the
 | |
| VFS id in the mount's idmapping into a userspace id in the caller's
 | |
| idmapping::
 | |
| 
 | |
|   k11000 = vfsuid_into_kuid(v11000)
 | |
|   from_kuid(u0:k10000:r10000, k11000) = u1000
 | |
| 
 | |
| We can test whether this algorithm really works by verifying what happens when
 | |
| we create a new file. Let's say the user is creating a file with ``u1000``.
 | |
| 
 | |
| The kernel maps this to ``k11000`` in the caller's idmapping. Usually the
 | |
| kernel would now apply the crossmapping, verifying that ``k11000`` can be
 | |
| mapped to a userspace id in the filesystem's idmapping. Since ``k11000`` can't
 | |
| be mapped up in the filesystem's idmapping directly this creation request
 | |
| fails.
 | |
| 
 | |
| But when the caller is accessing the file on an idmapped mount the kernel will
 | |
| first call ``mapped_fs*id()`` thereby translating the caller's kernel id into
 | |
| a VFS id according to the mount's idmapping::
 | |
| 
 | |
|  mapped_fsuid(k11000):
 | |
|     /* Map the caller's kernel id up into a userspace id in the mount's idmapping. */
 | |
|     from_kuid(u0:k10000:r10000, k11000) = u1000
 | |
| 
 | |
|     /* Map the mount's userspace id down into a kernel id in the filesystem's idmapping. */
 | |
|     make_kuid(u0:v20000:r10000, u1000) = v21000
 | |
| 
 | |
| When finally writing to disk the kernel will then map ``v21000`` up into a
 | |
| userspace id in the filesystem's idmapping::
 | |
| 
 | |
|    k21000 = vfsuid_into_kuid(v21000)
 | |
|    from_kuid(u0:k20000:r10000, k21000) = u1000
 | |
| 
 | |
| As we can see, we end up with an invertible and therefore information
 | |
| preserving algorithm. A file created from ``u1000`` on an idmapped mount will
 | |
| also be reported as being owned by ``u1000`` and vica versa.
 | |
| 
 | |
| Let's now briefly reconsider the failing examples from earlier in the context
 | |
| of idmapped mounts.
 | |
| 
 | |
| Example 2 reconsidered
 | |
| ~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| ::
 | |
| 
 | |
|  caller id:            u1000
 | |
|  caller idmapping:     u0:k10000:r10000
 | |
|  filesystem idmapping: u0:k20000:r10000
 | |
|  mount idmapping:      u0:v10000:r10000
 | |
| 
 | |
| When the caller is using a non-initial idmapping the common case is to attach
 | |
| the same idmapping to the mount. We now perform three steps:
 | |
| 
 | |
| 1. Map the caller's userspace ids into kernel ids in the caller's idmapping::
 | |
| 
 | |
|     make_kuid(u0:k10000:r10000, u1000) = k11000
 | |
| 
 | |
| 2. Translate the caller's VFS id into a kernel id in the filesystem's
 | |
|    idmapping::
 | |
| 
 | |
|     mapped_fsuid(v11000):
 | |
|       /* Map the VFS id up into a userspace id in the mount's idmapping. */
 | |
|       from_kuid(u0:v10000:r10000, v11000) = u1000
 | |
| 
 | |
|       /* Map the userspace id down into a kernel id in the filesystem's idmapping. */
 | |
|       make_kuid(u0:k20000:r10000, u1000) = k21000
 | |
| 
 | |
| 3. Verify that the caller's kernel ids can be mapped to userspace ids in the
 | |
|    filesystem's idmapping::
 | |
| 
 | |
|     from_kuid(u0:k20000:r10000, k21000) = u1000
 | |
| 
 | |
| So the ownership that lands on disk will be ``u1000``.
 | |
| 
 | |
| Example 3 reconsidered
 | |
| ~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| ::
 | |
| 
 | |
|  caller id:            u1000
 | |
|  caller idmapping:     u0:k10000:r10000
 | |
|  filesystem idmapping: u0:k0:r4294967295
 | |
|  mount idmapping:      u0:v10000:r10000
 | |
| 
 | |
| The same translation algorithm works with the third example.
 | |
| 
 | |
| 1. Map the caller's userspace ids into kernel ids in the caller's idmapping::
 | |
| 
 | |
|     make_kuid(u0:k10000:r10000, u1000) = k11000
 | |
| 
 | |
| 2. Translate the caller's VFS id into a kernel id in the filesystem's
 | |
|    idmapping::
 | |
| 
 | |
|     mapped_fsuid(v11000):
 | |
|        /* Map the VFS id up into a userspace id in the mount's idmapping. */
 | |
|        from_kuid(u0:v10000:r10000, v11000) = u1000
 | |
| 
 | |
|        /* Map the userspace id down into a kernel id in the filesystem's idmapping. */
 | |
|        make_kuid(u0:k0:r4294967295, u1000) = k1000
 | |
| 
 | |
| 3. Verify that the caller's kernel ids can be mapped to userspace ids in the
 | |
|    filesystem's idmapping::
 | |
| 
 | |
|     from_kuid(u0:k0:r4294967295, k1000) = u1000
 | |
| 
 | |
| So the ownership that lands on disk will be ``u1000``.
 | |
| 
 | |
| Example 4 reconsidered
 | |
| ~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| ::
 | |
| 
 | |
|  file id:              u1000
 | |
|  caller idmapping:     u0:k10000:r10000
 | |
|  filesystem idmapping: u0:k0:r4294967295
 | |
|  mount idmapping:      u0:v10000:r10000
 | |
| 
 | |
| In order to report ownership to userspace the kernel now does three steps using
 | |
| the translation algorithm we introduced earlier:
 | |
| 
 | |
| 1. Map the userspace id on disk down into a kernel id in the filesystem's
 | |
|    idmapping::
 | |
| 
 | |
|     make_kuid(u0:k0:r4294967295, u1000) = k1000
 | |
| 
 | |
| 2. Translate the kernel id into a VFS id in the mount's idmapping::
 | |
| 
 | |
|     i_uid_into_vfsuid(k1000):
 | |
|       /* Map the kernel id up into a userspace id in the filesystem's idmapping. */
 | |
|       from_kuid(u0:k0:r4294967295, k1000) = u1000
 | |
| 
 | |
|       /* Map the userspace id down into a VFS id in the mounts's idmapping. */
 | |
|       make_kuid(u0:v10000:r10000, u1000) = v11000
 | |
| 
 | |
| 3. Map the VFS id up into a userspace id in the caller's idmapping::
 | |
| 
 | |
|     k11000 = vfsuid_into_kuid(v11000)
 | |
|     from_kuid(u0:k10000:r10000, k11000) = u1000
 | |
| 
 | |
| Earlier, the caller's kernel id couldn't be crossmapped in the filesystems's
 | |
| idmapping. With the idmapped mount in place it now can be crossmapped into the
 | |
| filesystem's idmapping via the mount's idmapping. The file will now be created
 | |
| with ``u1000`` according to the mount's idmapping.
 | |
| 
 | |
| Example 5 reconsidered
 | |
| ~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| ::
 | |
| 
 | |
|  file id:              u1000
 | |
|  caller idmapping:     u0:k10000:r10000
 | |
|  filesystem idmapping: u0:k20000:r10000
 | |
|  mount idmapping:      u0:v10000:r10000
 | |
| 
 | |
| Again, in order to report ownership to userspace the kernel now does three
 | |
| steps using the translation algorithm we introduced earlier:
 | |
| 
 | |
| 1. Map the userspace id on disk down into a kernel id in the filesystem's
 | |
|    idmapping::
 | |
| 
 | |
|     make_kuid(u0:k20000:r10000, u1000) = k21000
 | |
| 
 | |
| 2. Translate the kernel id into a VFS id in the mount's idmapping::
 | |
| 
 | |
|     i_uid_into_vfsuid(k21000):
 | |
|       /* Map the kernel id up into a userspace id in the filesystem's idmapping. */
 | |
|       from_kuid(u0:k20000:r10000, k21000) = u1000
 | |
| 
 | |
|       /* Map the userspace id down into a VFS id in the mounts's idmapping. */
 | |
|       make_kuid(u0:v10000:r10000, u1000) = v11000
 | |
| 
 | |
| 3. Map the VFS id up into a userspace id in the caller's idmapping::
 | |
| 
 | |
|     k11000 = vfsuid_into_kuid(v11000)
 | |
|     from_kuid(u0:k10000:r10000, k11000) = u1000
 | |
| 
 | |
| Earlier, the file's kernel id couldn't be crossmapped in the filesystems's
 | |
| idmapping. With the idmapped mount in place it now can be crossmapped into the
 | |
| filesystem's idmapping via the mount's idmapping. The file is now owned by
 | |
| ``u1000`` according to the mount's idmapping.
 | |
| 
 | |
| Changing ownership on a home directory
 | |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 | |
| 
 | |
| We've seen above how idmapped mounts can be used to translate between
 | |
| idmappings when either the caller, the filesystem or both uses a non-initial
 | |
| idmapping. A wide range of usecases exist when the caller is using
 | |
| a non-initial idmapping. This mostly happens in the context of containerized
 | |
| workloads. The consequence is as we have seen that for both, filesystem's
 | |
| mounted with the initial idmapping and filesystems mounted with non-initial
 | |
| idmappings, access to the filesystem isn't working because the kernel ids can't
 | |
| be crossmapped between the caller's and the filesystem's idmapping.
 | |
| 
 | |
| As we've seen above idmapped mounts provide a solution to this by remapping the
 | |
| caller's or filesystem's idmapping according to the mount's idmapping.
 | |
| 
 | |
| Aside from containerized workloads, idmapped mounts have the advantage that
 | |
| they also work when both the caller and the filesystem use the initial
 | |
| idmapping which means users on the host can change the ownership of directories
 | |
| and files on a per-mount basis.
 | |
| 
 | |
| Consider our previous example where a user has their home directory on portable
 | |
| storage. At home they have id ``u1000`` and all files in their home directory
 | |
| are owned by ``u1000`` whereas at uni or work they have login id ``u1125``.
 | |
| 
 | |
| Taking their home directory with them becomes problematic. They can't easily
 | |
| access their files, they might not be able to write to disk without applying
 | |
| lax permissions or ACLs and even if they can, they will end up with an annoying
 | |
| mix of files and directories owned by ``u1000`` and ``u1125``.
 | |
| 
 | |
| Idmapped mounts allow to solve this problem. A user can create an idmapped
 | |
| mount for their home directory on their work computer or their computer at home
 | |
| depending on what ownership they would prefer to end up on the portable storage
 | |
| itself.
 | |
| 
 | |
| Let's assume they want all files on disk to belong to ``u1000``. When the user
 | |
| plugs in their portable storage at their work station they can setup a job that
 | |
| creates an idmapped mount with the minimal idmapping ``u1000:k1125:r1``. So now
 | |
| when they create a file the kernel performs the following steps we already know
 | |
| from above:::
 | |
| 
 | |
|  caller id:            u1125
 | |
|  caller idmapping:     u0:k0:r4294967295
 | |
|  filesystem idmapping: u0:k0:r4294967295
 | |
|  mount idmapping:      u1000:v1125:r1
 | |
| 
 | |
| 1. Map the caller's userspace ids into kernel ids in the caller's idmapping::
 | |
| 
 | |
|     make_kuid(u0:k0:r4294967295, u1125) = k1125
 | |
| 
 | |
| 2. Translate the caller's VFS id into a kernel id in the filesystem's
 | |
|    idmapping::
 | |
| 
 | |
|     mapped_fsuid(v1125):
 | |
|       /* Map the VFS id up into a userspace id in the mount's idmapping. */
 | |
|       from_kuid(u1000:v1125:r1, v1125) = u1000
 | |
| 
 | |
|       /* Map the userspace id down into a kernel id in the filesystem's idmapping. */
 | |
|       make_kuid(u0:k0:r4294967295, u1000) = k1000
 | |
| 
 | |
| 3. Verify that the caller's filesystem ids can be mapped to userspace ids in the
 | |
|    filesystem's idmapping::
 | |
| 
 | |
|     from_kuid(u0:k0:r4294967295, k1000) = u1000
 | |
| 
 | |
| So ultimately the file will be created with ``u1000`` on disk.
 | |
| 
 | |
| Now let's briefly look at what ownership the caller with id ``u1125`` will see
 | |
| on their work computer:
 | |
| 
 | |
| ::
 | |
| 
 | |
|  file id:              u1000
 | |
|  caller idmapping:     u0:k0:r4294967295
 | |
|  filesystem idmapping: u0:k0:r4294967295
 | |
|  mount idmapping:      u1000:v1125:r1
 | |
| 
 | |
| 1. Map the userspace id on disk down into a kernel id in the filesystem's
 | |
|    idmapping::
 | |
| 
 | |
|     make_kuid(u0:k0:r4294967295, u1000) = k1000
 | |
| 
 | |
| 2. Translate the kernel id into a VFS id in the mount's idmapping::
 | |
| 
 | |
|     i_uid_into_vfsuid(k1000):
 | |
|       /* Map the kernel id up into a userspace id in the filesystem's idmapping. */
 | |
|       from_kuid(u0:k0:r4294967295, k1000) = u1000
 | |
| 
 | |
|       /* Map the userspace id down into a VFS id in the mounts's idmapping. */
 | |
|       make_kuid(u1000:v1125:r1, u1000) = v1125
 | |
| 
 | |
| 3. Map the VFS id up into a userspace id in the caller's idmapping::
 | |
| 
 | |
|     k1125 = vfsuid_into_kuid(v1125)
 | |
|     from_kuid(u0:k0:r4294967295, k1125) = u1125
 | |
| 
 | |
| So ultimately the caller will be reported that the file belongs to ``u1125``
 | |
| which is the caller's userspace id on their workstation in our example.
 | |
| 
 | |
| The raw userspace id that is put on disk is ``u1000`` so when the user takes
 | |
| their home directory back to their home computer where they are assigned
 | |
| ``u1000`` using the initial idmapping and mount the filesystem with the initial
 | |
| idmapping they will see all those files owned by ``u1000``.
 |