cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

autofs-mount-control.rst (18545B)


      1.. SPDX-License-Identifier: GPL-2.0
      2
      3====================================================================
      4Miscellaneous Device control operations for the autofs kernel module
      5====================================================================
      6
      7The problem
      8===========
      9
     10There is a problem with active restarts in autofs (that is to say
     11restarting autofs when there are busy mounts).
     12
     13During normal operation autofs uses a file descriptor opened on the
     14directory that is being managed in order to be able to issue control
     15operations. Using a file descriptor gives ioctl operations access to
     16autofs specific information stored in the super block. The operations
     17are things such as setting an autofs mount catatonic, setting the
     18expire timeout and requesting expire checks. As is explained below,
     19certain types of autofs triggered mounts can end up covering an autofs
     20mount itself which prevents us being able to use open(2) to obtain a
     21file descriptor for these operations if we don't already have one open.
     22
     23Currently autofs uses "umount -l" (lazy umount) to clear active mounts
     24at restart. While using lazy umount works for most cases, anything that
     25needs to walk back up the mount tree to construct a path, such as
     26getcwd(2) and the proc file system /proc/<pid>/cwd, no longer works
     27because the point from which the path is constructed has been detached
     28from the mount tree.
     29
     30The actual problem with autofs is that it can't reconnect to existing
     31mounts. Immediately one thinks of just adding the ability to remount
     32autofs file systems would solve it, but alas, that can't work. This is
     33because autofs direct mounts and the implementation of "on demand mount
     34and expire" of nested mount trees have the file system mounted directly
     35on top of the mount trigger directory dentry.
     36
     37For example, there are two types of automount maps, direct (in the kernel
     38module source you will see a third type called an offset, which is just
     39a direct mount in disguise) and indirect.
     40
     41Here is a master map with direct and indirect map entries::
     42
     43    /-      /etc/auto.direct
     44    /test   /etc/auto.indirect
     45
     46and the corresponding map files::
     47
     48    /etc/auto.direct:
     49
     50    /automount/dparse/g6  budgie:/autofs/export1
     51    /automount/dparse/g1  shark:/autofs/export1
     52    and so on.
     53
     54/etc/auto.indirect::
     55
     56    g1    shark:/autofs/export1
     57    g6    budgie:/autofs/export1
     58    and so on.
     59
     60For the above indirect map an autofs file system is mounted on /test and
     61mounts are triggered for each sub-directory key by the inode lookup
     62operation. So we see a mount of shark:/autofs/export1 on /test/g1, for
     63example.
     64
     65The way that direct mounts are handled is by making an autofs mount on
     66each full path, such as /automount/dparse/g1, and using it as a mount
     67trigger. So when we walk on the path we mount shark:/autofs/export1 "on
     68top of this mount point". Since these are always directories we can
     69use the follow_link inode operation to trigger the mount.
     70
     71But, each entry in direct and indirect maps can have offsets (making
     72them multi-mount map entries).
     73
     74For example, an indirect mount map entry could also be::
     75
     76    g1  \
     77    /        shark:/autofs/export5/testing/test \
     78    /s1      shark:/autofs/export/testing/test/s1 \
     79    /s2      shark:/autofs/export5/testing/test/s2 \
     80    /s1/ss1  shark:/autofs/export1 \
     81    /s2/ss2  shark:/autofs/export2
     82
     83and a similarly a direct mount map entry could also be::
     84
     85    /automount/dparse/g1 \
     86	/       shark:/autofs/export5/testing/test \
     87	/s1     shark:/autofs/export/testing/test/s1 \
     88	/s2     shark:/autofs/export5/testing/test/s2 \
     89	/s1/ss1 shark:/autofs/export2 \
     90	/s2/ss2 shark:/autofs/export2
     91
     92One of the issues with version 4 of autofs was that, when mounting an
     93entry with a large number of offsets, possibly with nesting, we needed
     94to mount and umount all of the offsets as a single unit. Not really a
     95problem, except for people with a large number of offsets in map entries.
     96This mechanism is used for the well known "hosts" map and we have seen
     97cases (in 2.4) where the available number of mounts are exhausted or
     98where the number of privileged ports available is exhausted.
     99
    100In version 5 we mount only as we go down the tree of offsets and
    101similarly for expiring them which resolves the above problem. There is
    102somewhat more detail to the implementation but it isn't needed for the
    103sake of the problem explanation. The one important detail is that these
    104offsets are implemented using the same mechanism as the direct mounts
    105above and so the mount points can be covered by a mount.
    106
    107The current autofs implementation uses an ioctl file descriptor opened
    108on the mount point for control operations. The references held by the
    109descriptor are accounted for in checks made to determine if a mount is
    110in use and is also used to access autofs file system information held
    111in the mount super block. So the use of a file handle needs to be
    112retained.
    113
    114
    115The Solution
    116============
    117
    118To be able to restart autofs leaving existing direct, indirect and
    119offset mounts in place we need to be able to obtain a file handle
    120for these potentially covered autofs mount points. Rather than just
    121implement an isolated operation it was decided to re-implement the
    122existing ioctl interface and add new operations to provide this
    123functionality.
    124
    125In addition, to be able to reconstruct a mount tree that has busy mounts,
    126the uid and gid of the last user that triggered the mount needs to be
    127available because these can be used as macro substitution variables in
    128autofs maps. They are recorded at mount request time and an operation
    129has been added to retrieve them.
    130
    131Since we're re-implementing the control interface, a couple of other
    132problems with the existing interface have been addressed. First, when
    133a mount or expire operation completes a status is returned to the
    134kernel by either a "send ready" or a "send fail" operation. The
    135"send fail" operation of the ioctl interface could only ever send
    136ENOENT so the re-implementation allows user space to send an actual
    137status. Another expensive operation in user space, for those using
    138very large maps, is discovering if a mount is present. Usually this
    139involves scanning /proc/mounts and since it needs to be done quite
    140often it can introduce significant overhead when there are many entries
    141in the mount table. An operation to lookup the mount status of a mount
    142point dentry (covered or not) has also been added.
    143
    144Current kernel development policy recommends avoiding the use of the
    145ioctl mechanism in favor of systems such as Netlink. An implementation
    146using this system was attempted to evaluate its suitability and it was
    147found to be inadequate, in this case. The Generic Netlink system was
    148used for this as raw Netlink would lead to a significant increase in
    149complexity. There's no question that the Generic Netlink system is an
    150elegant solution for common case ioctl functions but it's not a complete
    151replacement probably because its primary purpose in life is to be a
    152message bus implementation rather than specifically an ioctl replacement.
    153While it would be possible to work around this there is one concern
    154that lead to the decision to not use it. This is that the autofs
    155expire in the daemon has become far to complex because umount
    156candidates are enumerated, almost for no other reason than to "count"
    157the number of times to call the expire ioctl. This involves scanning
    158the mount table which has proved to be a big overhead for users with
    159large maps. The best way to improve this is try and get back to the
    160way the expire was done long ago. That is, when an expire request is
    161issued for a mount (file handle) we should continually call back to
    162the daemon until we can't umount any more mounts, then return the
    163appropriate status to the daemon. At the moment we just expire one
    164mount at a time. A Generic Netlink implementation would exclude this
    165possibility for future development due to the requirements of the
    166message bus architecture.
    167
    168
    169autofs Miscellaneous Device mount control interface
    170====================================================
    171
    172The control interface is opening a device node, typically /dev/autofs.
    173
    174All the ioctls use a common structure to pass the needed parameter
    175information and return operation results::
    176
    177    struct autofs_dev_ioctl {
    178	    __u32 ver_major;
    179	    __u32 ver_minor;
    180	    __u32 size;             /* total size of data passed in
    181				    * including this struct */
    182	    __s32 ioctlfd;          /* automount command fd */
    183
    184	    /* Command parameters */
    185	    union {
    186		    struct args_protover		protover;
    187		    struct args_protosubver		protosubver;
    188		    struct args_openmount		openmount;
    189		    struct args_ready		ready;
    190		    struct args_fail		fail;
    191		    struct args_setpipefd		setpipefd;
    192		    struct args_timeout		timeout;
    193		    struct args_requester		requester;
    194		    struct args_expire		expire;
    195		    struct args_askumount		askumount;
    196		    struct args_ismountpoint	ismountpoint;
    197	    };
    198
    199	    char path[0];
    200    };
    201
    202The ioctlfd field is a mount point file descriptor of an autofs mount
    203point. It is returned by the open call and is used by all calls except
    204the check for whether a given path is a mount point, where it may
    205optionally be used to check a specific mount corresponding to a given
    206mount point file descriptor, and when requesting the uid and gid of the
    207last successful mount on a directory within the autofs file system.
    208
    209The union is used to communicate parameters and results of calls made
    210as described below.
    211
    212The path field is used to pass a path where it is needed and the size field
    213is used account for the increased structure length when translating the
    214structure sent from user space.
    215
    216This structure can be initialized before setting specific fields by using
    217the void function call init_autofs_dev_ioctl(``struct autofs_dev_ioctl *``).
    218
    219All of the ioctls perform a copy of this structure from user space to
    220kernel space and return -EINVAL if the size parameter is smaller than
    221the structure size itself, -ENOMEM if the kernel memory allocation fails
    222or -EFAULT if the copy itself fails. Other checks include a version check
    223of the compiled in user space version against the module version and a
    224mismatch results in a -EINVAL return. If the size field is greater than
    225the structure size then a path is assumed to be present and is checked to
    226ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is
    227returned. Following these checks, for all ioctl commands except
    228AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and
    229AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is
    230not a valid descriptor or doesn't correspond to an autofs mount point
    231an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is
    232returned.
    233
    234
    235The ioctls
    236==========
    237
    238An example of an implementation which uses this interface can be seen
    239in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the
    240distribution tar available for download from kernel.org in directory
    241/pub/linux/daemons/autofs/v5.
    242
    243The device node ioctl operations implemented by this interface are:
    244
    245
    246AUTOFS_DEV_IOCTL_VERSION
    247------------------------
    248
    249Get the major and minor version of the autofs device ioctl kernel module
    250implementation. It requires an initialized struct autofs_dev_ioctl as an
    251input parameter and sets the version information in the passed in structure.
    252It returns 0 on success or the error -EINVAL if a version mismatch is
    253detected.
    254
    255
    256AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD
    257------------------------------------------------------------------
    258
    259Get the major and minor version of the autofs protocol version understood
    260by loaded module. This call requires an initialized struct autofs_dev_ioctl
    261with the ioctlfd field set to a valid autofs mount point descriptor
    262and sets the requested version number in version field of struct args_protover
    263or sub_version field of struct args_protosubver. These commands return
    2640 on success or one of the negative error codes if validation fails.
    265
    266
    267AUTOFS_DEV_IOCTL_OPENMOUNT and AUTOFS_DEV_IOCTL_CLOSEMOUNT
    268----------------------------------------------------------
    269
    270Obtain and release a file descriptor for an autofs managed mount point
    271path. The open call requires an initialized struct autofs_dev_ioctl with
    272the path field set and the size field adjusted appropriately as well
    273as the devid field of struct args_openmount set to the device number of
    274the autofs mount. The device number can be obtained from the mount options
    275shown in /proc/mounts. The close call requires an initialized struct
    276autofs_dev_ioct with the ioctlfd field set to the descriptor obtained
    277from the open call. The release of the file descriptor can also be done
    278with close(2) so any open descriptors will also be closed at process exit.
    279The close call is included in the implemented operations largely for
    280completeness and to provide for a consistent user space implementation.
    281
    282
    283AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD
    284--------------------------------------------------------
    285
    286Return mount and expire result status from user space to the kernel.
    287Both of these calls require an initialized struct autofs_dev_ioctl
    288with the ioctlfd field set to the descriptor obtained from the open
    289call and the token field of struct args_ready or struct args_fail set
    290to the wait queue token number, received by user space in the foregoing
    291mount or expire request. The status field of struct args_fail is set to
    292the errno of the operation. It is set to 0 on success.
    293
    294
    295AUTOFS_DEV_IOCTL_SETPIPEFD_CMD
    296------------------------------
    297
    298Set the pipe file descriptor used for kernel communication to the daemon.
    299Normally this is set at mount time using an option but when reconnecting
    300to a existing mount we need to use this to tell the autofs mount about
    301the new kernel pipe descriptor. In order to protect mounts against
    302incorrectly setting the pipe descriptor we also require that the autofs
    303mount be catatonic (see next call).
    304
    305The call requires an initialized struct autofs_dev_ioctl with the
    306ioctlfd field set to the descriptor obtained from the open call and
    307the pipefd field of struct args_setpipefd set to descriptor of the pipe.
    308On success the call also sets the process group id used to identify the
    309controlling process (eg. the owning automount(8) daemon) to the process
    310group of the caller.
    311
    312
    313AUTOFS_DEV_IOCTL_CATATONIC_CMD
    314------------------------------
    315
    316Make the autofs mount point catatonic. The autofs mount will no longer
    317issue mount requests, the kernel communication pipe descriptor is released
    318and any remaining waits in the queue released.
    319
    320The call requires an initialized struct autofs_dev_ioctl with the
    321ioctlfd field set to the descriptor obtained from the open call.
    322
    323
    324AUTOFS_DEV_IOCTL_TIMEOUT_CMD
    325----------------------------
    326
    327Set the expire timeout for mounts within an autofs mount point.
    328
    329The call requires an initialized struct autofs_dev_ioctl with the
    330ioctlfd field set to the descriptor obtained from the open call.
    331
    332
    333AUTOFS_DEV_IOCTL_REQUESTER_CMD
    334------------------------------
    335
    336Return the uid and gid of the last process to successfully trigger a the
    337mount on the given path dentry.
    338
    339The call requires an initialized struct autofs_dev_ioctl with the path
    340field set to the mount point in question and the size field adjusted
    341appropriately. Upon return the uid field of struct args_requester contains
    342the uid and gid field the gid.
    343
    344When reconstructing an autofs mount tree with active mounts we need to
    345re-connect to mounts that may have used the original process uid and
    346gid (or string variations of them) for mount lookups within the map entry.
    347This call provides the ability to obtain this uid and gid so they may be
    348used by user space for the mount map lookups.
    349
    350
    351AUTOFS_DEV_IOCTL_EXPIRE_CMD
    352---------------------------
    353
    354Issue an expire request to the kernel for an autofs mount. Typically
    355this ioctl is called until no further expire candidates are found.
    356
    357The call requires an initialized struct autofs_dev_ioctl with the
    358ioctlfd field set to the descriptor obtained from the open call. In
    359addition an immediate expire that's independent of the mount timeout,
    360and a forced expire that's independent of whether the mount is busy,
    361can be requested by setting the how field of struct args_expire to
    362AUTOFS_EXP_IMMEDIATE or AUTOFS_EXP_FORCED, respectively . If no
    363expire candidates can be found the ioctl returns -1 with errno set to
    364EAGAIN.
    365
    366This call causes the kernel module to check the mount corresponding
    367to the given ioctlfd for mounts that can be expired, issues an expire
    368request back to the daemon and waits for completion.
    369
    370AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD
    371------------------------------
    372
    373Checks if an autofs mount point is in use.
    374
    375The call requires an initialized struct autofs_dev_ioctl with the
    376ioctlfd field set to the descriptor obtained from the open call and
    377it returns the result in the may_umount field of struct args_askumount,
    3781 for busy and 0 otherwise.
    379
    380
    381AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD
    382---------------------------------
    383
    384Check if the given path is a mountpoint.
    385
    386The call requires an initialized struct autofs_dev_ioctl. There are two
    387possible variations. Both use the path field set to the path of the mount
    388point to check and the size field adjusted appropriately. One uses the
    389ioctlfd field to identify a specific mount point to check while the other
    390variation uses the path and optionally in.type field of struct args_ismountpoint
    391set to an autofs mount type. The call returns 1 if this is a mount point
    392and sets out.devid field to the device number of the mount and out.magic
    393field to the relevant super block magic number (described below) or 0 if
    394it isn't a mountpoint. In both cases the device number (as returned
    395by new_encode_dev()) is returned in out.devid field.
    396
    397If supplied with a file descriptor we're looking for a specific mount,
    398not necessarily at the top of the mounted stack. In this case the path
    399the descriptor corresponds to is considered a mountpoint if it is itself
    400a mountpoint or contains a mount, such as a multi-mount without a root
    401mount. In this case we return 1 if the descriptor corresponds to a mount
    402point and also returns the super magic of the covering mount if there
    403is one or 0 if it isn't a mountpoint.
    404
    405If a path is supplied (and the ioctlfd field is set to -1) then the path
    406is looked up and is checked to see if it is the root of a mount. If a
    407type is also given we are looking for a particular autofs mount and if
    408a match isn't found a fail is returned. If the located path is the
    409root of a mount 1 is returned along with the super magic of the mount
    410or 0 otherwise.