Merge branch 'akpm' (patches from Andrew)
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 8 Jun 2018 01:39:37 +0000 (18:39 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 8 Jun 2018 01:39:37 +0000 (18:39 -0700)
Merge updates from Andrew Morton:

 - a few misc things

 - ocfs2 updates

 - v9fs updates

 - MM

 - procfs updates

 - lib/ updates

 - autofs updates

* emailed patches from Andrew Morton <akpm@linux-foundation.org>: (118 commits)
  autofs: small cleanup in autofs_getpath()
  autofs: clean up includes
  autofs: comment on selinux changes needed for module autoload
  autofs: update MAINTAINERS entry for autofs
  autofs: use autofs instead of autofs4 in documentation
  autofs: rename autofs documentation files
  autofs: create autofs Kconfig and Makefile
  autofs: delete fs/autofs4 source files
  autofs: update fs/autofs4/Makefile
  autofs: update fs/autofs4/Kconfig
  autofs: copy autofs4 to autofs
  autofs4: use autofs instead of autofs4 everywhere
  autofs4: merge auto_fs.h and auto_fs4.h
  fs/binfmt_misc.c: do not allow offset overflow
  checkpatch: improve patch recognition
  lib/ucs2_string.c: add MODULE_LICENSE()
  lib/mpi: headers cleanup
  lib/percpu_ida.c: use _irqsave() instead of local_irq_save() + spin_lock
  lib/idr.c: remove simple_ida_lock
  lib/bitmap.c: micro-optimization for __bitmap_complement()
  ...

157 files changed:
Documentation/admin-guide/cgroup-v2.rst
Documentation/blockdev/zram.txt
Documentation/features/vm/pte_special/arch-support.txt
Documentation/filesystems/00-INDEX
Documentation/filesystems/autofs-mount-control.txt [new file with mode: 0644]
Documentation/filesystems/autofs.txt [new file with mode: 0644]
Documentation/filesystems/autofs4-mount-control.txt [deleted file]
Documentation/filesystems/autofs4.txt [deleted file]
Documentation/filesystems/automount-support.txt
Documentation/filesystems/path-lookup.md
MAINTAINERS
arch/arc/Kconfig
arch/arc/include/asm/pgtable.h
arch/arm/Kconfig
arch/arm/include/asm/pgtable-3level.h
arch/arm64/Kconfig
arch/arm64/include/asm/pgtable.h
arch/powerpc/Kconfig
arch/powerpc/include/asm/book3s/64/pgtable.h
arch/powerpc/include/asm/pte-common.h
arch/riscv/Kconfig
arch/riscv/include/asm/pgtable-bits.h
arch/s390/Kconfig
arch/s390/include/asm/pgtable.h
arch/s390/mm/pgalloc.c
arch/sh/Kconfig
arch/sh/include/asm/pgtable.h
arch/sparc/Kconfig
arch/sparc/include/asm/pgtable_64.h
arch/x86/Kconfig
arch/x86/include/asm/pgtable_types.h
arch/x86/mm/pgtable.c
drivers/block/zram/Kconfig
drivers/block/zram/zram_drv.c
drivers/block/zram/zram_drv.h
fs/9p/v9fs.c
fs/Kconfig
fs/Makefile
fs/autofs/Kconfig [new file with mode: 0644]
fs/autofs/Makefile [new file with mode: 0644]
fs/autofs/autofs_i.h [new file with mode: 0644]
fs/autofs/dev-ioctl.c [new file with mode: 0644]
fs/autofs/expire.c [new file with mode: 0644]
fs/autofs/init.c [new file with mode: 0644]
fs/autofs/inode.c [new file with mode: 0644]
fs/autofs/root.c [new file with mode: 0644]
fs/autofs/symlink.c [new file with mode: 0644]
fs/autofs/waitq.c [new file with mode: 0644]
fs/autofs4/Kconfig
fs/autofs4/Makefile
fs/autofs4/autofs_i.h [deleted file]
fs/autofs4/dev-ioctl.c [deleted file]
fs/autofs4/expire.c [deleted file]
fs/autofs4/init.c [deleted file]
fs/autofs4/inode.c [deleted file]
fs/autofs4/root.c [deleted file]
fs/autofs4/symlink.c [deleted file]
fs/autofs4/waitq.c [deleted file]
fs/binfmt_misc.c
fs/compat_ioctl.c
fs/dax.c
fs/fcntl.c
fs/ocfs2/dlmglue.c
fs/ocfs2/dlmglue.h
fs/ocfs2/file.c
fs/ocfs2/file.h
fs/ocfs2/ioctl.c
fs/ocfs2/mmap.c
fs/ocfs2/namei.c
fs/ocfs2/ocfs2_fs.h
fs/proc/array.c
fs/proc/base.c
fs/proc/fd.c
fs/proc/internal.h
fs/proc/page.c
fs/proc/task_mmu.c
fs/userfaultfd.c
include/asm-generic/int-ll64.h
include/linux/dax.h
include/linux/gfp.h
include/linux/hmm.h
include/linux/kernel.h
include/linux/ksm.h
include/linux/memcontrol.h
include/linux/memfd.h [new file with mode: 0644]
include/linux/memory_hotplug.h
include/linux/mm.h
include/linux/mm_types.h
include/linux/mpi.h
include/linux/page-flags.h
include/linux/page_counter.h
include/linux/pfn_t.h
include/linux/sched/mm.h
include/linux/shmem_fs.h
include/linux/slab_def.h
include/linux/slub_def.h
include/linux/types.h
include/linux/userfaultfd_k.h
include/uapi/linux/auto_fs.h
include/uapi/linux/auto_fs4.h
include/uapi/linux/kernel-page-flags.h
kernel/crash_core.c
kernel/fork.c
kernel/hung_task.c
kernel/sys.c
lib/bitmap.c
lib/bucket_locks.c
lib/idr.c
lib/mpi/mpi-internal.h
lib/percpu_ida.c
lib/ucs2_string.c
mm/Kconfig
mm/Makefile
mm/backing-dev.c
mm/filemap.c
mm/gup.c
mm/huge_memory.c
mm/hugetlb.c
mm/hugetlb_cgroup.c
mm/init-mm.c
mm/ksm.c
mm/memblock.c
mm/memcontrol.c
mm/memfd.c [new file with mode: 0644]
mm/memory.c
mm/memory_hotplug.c
mm/mmap.c
mm/nommu.c
mm/oom_kill.c
mm/page_alloc.c
mm/page_counter.c
mm/shmem.c
mm/slab.c
mm/slob.c
mm/slub.c
mm/sparse.c
mm/swap_slots.c
mm/swap_state.c
mm/userfaultfd.c
mm/util.c
mm/vmalloc.c
mm/vmpressure.c
mm/vmscan.c
net/9p/client.c
net/9p/trans_xen.c
scripts/checkpatch.pl
scripts/get_maintainer.pl
scripts/tags.sh
tools/testing/selftests/proc/.gitignore
tools/testing/selftests/proc/Makefile
tools/testing/selftests/proc/fd-001-lookup.c [new file with mode: 0644]
tools/testing/selftests/proc/fd-002-posix-eq.c [new file with mode: 0644]
tools/testing/selftests/proc/fd-003-kthread.c [new file with mode: 0644]
tools/testing/selftests/proc/proc-uptime.h
tools/testing/selftests/proc/proc.h [new file with mode: 0644]
tools/testing/selftests/proc/read.c
tools/vm/page-types.c

index 74cdeaed9f7afb4f0b514f1213b38b2eda106f56..8a2c52d5c53b7aaa9c2fcc5b684c0ac3dbcd53dc 100644 (file)
@@ -1001,14 +1001,44 @@ PAGE_SIZE multiple when read back.
        The total amount of memory currently being used by the cgroup
        and its descendants.
 
+  memory.min
+       A read-write single value file which exists on non-root
+       cgroups.  The default is "0".
+
+       Hard memory protection.  If the memory usage of a cgroup
+       is within its effective min boundary, the cgroup's memory
+       won't be reclaimed under any conditions. If there is no
+       unprotected reclaimable memory available, OOM killer
+       is invoked.
+
+       Effective min boundary is limited by memory.min values of
+       all ancestor cgroups. If there is memory.min overcommitment
+       (child cgroup or cgroups are requiring more protected memory
+       than parent will allow), then each child cgroup will get
+       the part of parent's protection proportional to its
+       actual memory usage below memory.min.
+
+       Putting more memory than generally available under this
+       protection is discouraged and may lead to constant OOMs.
+
+       If a memory cgroup is not populated with processes,
+       its memory.min is ignored.
+
   memory.low
        A read-write single value file which exists on non-root
        cgroups.  The default is "0".
 
-       Best-effort memory protection.  If the memory usages of a
-       cgroup and all its ancestors are below their low boundaries,
-       the cgroup's memory won't be reclaimed unless memory can be
-       reclaimed from unprotected cgroups.
+       Best-effort memory protection.  If the memory usage of a
+       cgroup is within its effective low boundary, the cgroup's
+       memory won't be reclaimed unless memory can be reclaimed
+       from unprotected cgroups.
+
+       Effective low boundary is limited by memory.low values of
+       all ancestor cgroups. If there is memory.low overcommitment
+       (child cgroup or cgroups are requiring more protected memory
+       than parent will allow), then each child cgroup will get
+       the part of parent's protection proportional to its
+       actual memory usage below memory.low.
 
        Putting more memory than generally available under this
        protection is discouraged.
@@ -1199,6 +1229,27 @@ PAGE_SIZE multiple when read back.
        Swap usage hard limit.  If a cgroup's swap usage reaches this
        limit, anonymous memory of the cgroup will not be swapped out.
 
+  memory.swap.events
+       A read-only flat-keyed file which exists on non-root cgroups.
+       The following entries are defined.  Unless specified
+       otherwise, a value change in this file generates a file
+       modified event.
+
+         max
+               The number of times the cgroup's swap usage was about
+               to go over the max boundary and swap allocation
+               failed.
+
+         fail
+               The number of times swap allocation failed either
+               because of running out of swap system-wide or max
+               limit.
+
+       When reduced under the current usage, the existing swap
+       entries are reclaimed gradually and the swap usage may stay
+       higher than the limit for an extended period of time.  This
+       reduces the impact on the workload and memory management.
+
 
 Usage Guidelines
 ~~~~~~~~~~~~~~~~
@@ -1934,17 +1985,8 @@ system performance due to overreclaim, to the point where the feature
 becomes self-defeating.
 
 The memory.low boundary on the other hand is a top-down allocated
-reserve.  A cgroup enjoys reclaim protection when it and all its
-ancestors are below their low boundaries, which makes delegation of
-subtrees possible.  Secondly, new cgroups have no reserve per default
-and in the common case most cgroups are eligible for the preferred
-reclaim pass.  This allows the new low boundary to be efficiently
-implemented with just a minor addition to the generic reclaim code,
-without the need for out-of-band data structures and reclaim passes.
-Because the generic reclaim code considers all cgroups except for the
-ones running low in the preferred first reclaim pass, overreclaim of
-individual groups is eliminated as well, resulting in much better
-overall workload performance.
+reserve.  A cgroup enjoys reclaim protection when it's within its low,
+which makes delegation of subtrees possible.
 
 The original high boundary, the hard limit, is defined as a strict
 limit that can not budge, even if the OOM killer has to be called.
index 257e65714c6a216f3fce9ebce7398eb821f96176..875b2b56b87fc88131324bb10bbecc8594e02109 100644 (file)
@@ -218,6 +218,7 @@ line of text and contains the following stats separated by whitespace:
  same_pages       the number of same element filled pages written to this disk.
                   No memory is allocated for such pages.
  pages_compacted  the number of pages freed during compaction
+ huge_pages      the number of incompressible pages
 
 9) Deactivate:
        swapoff /dev/zram0
@@ -242,5 +243,29 @@ to backing storage rather than keeping it in memory.
 User should set up backing device via /sys/block/zramX/backing_dev
 before disksize setting.
 
+= memory tracking
+
+With CONFIG_ZRAM_MEMORY_TRACKING, user can know information of the
+zram block. It could be useful to catch cold or incompressible
+pages of the process with*pagemap.
+If you enable the feature, you could see block state via
+/sys/kernel/debug/zram/zram0/block_state". The output is as follows,
+
+         300    75.033841 .wh
+         301    63.806904 s..
+         302    63.806919 ..h
+
+First column is zram's block index.
+Second column is access time since the system was booted
+Third column is state of the block.
+(s: same page
+w: written page to backing store
+h: huge page)
+
+First line of above example says 300th block is accessed at 75.033841sec
+and the block's state is huge so it is written back to the backing
+storage. It's a debugging feature so anyone shouldn't rely on it to work
+properly.
+
 Nitin Gupta
 ngupta@vflare.org
index 6a608a6dcf71ded9919fbf7c4e621e592e9a9ab5..a8378424bc98450563e14cce59da34008001ca5d 100644 (file)
@@ -1,6 +1,6 @@
 #
 # Feature name:          pte_special
-#         Kconfig:       __HAVE_ARCH_PTE_SPECIAL
+#         Kconfig:       ARCH_HAS_PTE_SPECIAL
 #         description:   arch supports the pte_special()/pte_mkspecial() VM APIs
 #
     -----------------------
index b7bd6c9009ccb1485d95075221c0ce8dc016ebe2..a8bd4af7fbcebf98e93ded3814b67e556d7599ae 100644 (file)
@@ -10,8 +10,8 @@ afs.txt
        - info and examples for the distributed AFS (Andrew File System) fs.
 affs.txt
        - info and mount options for the Amiga Fast File System.
-autofs4-mount-control.txt
-       - info on device control operations for autofs4 module.
+autofs-mount-control.txt
+       - info on device control operations for autofs module.
 automount-support.txt
        - information about filesystem automount support.
 befs.txt
diff --git a/Documentation/filesystems/autofs-mount-control.txt b/Documentation/filesystems/autofs-mount-control.txt
new file mode 100644 (file)
index 0000000..45edad6
--- /dev/null
@@ -0,0 +1,406 @@
+
+Miscellaneous Device control operations for the autofs kernel module
+====================================================================
+
+The problem
+===========
+
+There is a problem with active restarts in autofs (that is to say
+restarting autofs when there are busy mounts).
+
+During normal operation autofs uses a file descriptor opened on the
+directory that is being managed in order to be able to issue control
+operations. Using a file descriptor gives ioctl operations access to
+autofs specific information stored in the super block. The operations
+are things such as setting an autofs mount catatonic, setting the
+expire timeout and requesting expire checks. As is explained below,
+certain types of autofs triggered mounts can end up covering an autofs
+mount itself which prevents us being able to use open(2) to obtain a
+file descriptor for these operations if we don't already have one open.
+
+Currently autofs uses "umount -l" (lazy umount) to clear active mounts
+at restart. While using lazy umount works for most cases, anything that
+needs to walk back up the mount tree to construct a path, such as
+getcwd(2) and the proc file system /proc/<pid>/cwd, no longer works
+because the point from which the path is constructed has been detached
+from the mount tree.
+
+The actual problem with autofs is that it can't reconnect to existing
+mounts. Immediately one thinks of just adding the ability to remount
+autofs file systems would solve it, but alas, that can't work. This is
+because autofs direct mounts and the implementation of "on demand mount
+and expire" of nested mount trees have the file system mounted directly
+on top of the mount trigger directory dentry.
+
+For example, there are two types of automount maps, direct (in the kernel
+module source you will see a third type called an offset, which is just
+a direct mount in disguise) and indirect.
+
+Here is a master map with direct and indirect map entries:
+
+/-      /etc/auto.direct
+/test   /etc/auto.indirect
+
+and the corresponding map files:
+
+/etc/auto.direct:
+
+/automount/dparse/g6  budgie:/autofs/export1
+/automount/dparse/g1  shark:/autofs/export1
+and so on.
+
+/etc/auto.indirect:
+
+g1    shark:/autofs/export1
+g6    budgie:/autofs/export1
+and so on.
+
+For the above indirect map an autofs file system is mounted on /test and
+mounts are triggered for each sub-directory key by the inode lookup
+operation. So we see a mount of shark:/autofs/export1 on /test/g1, for
+example.
+
+The way that direct mounts are handled is by making an autofs mount on
+each full path, such as /automount/dparse/g1, and using it as a mount
+trigger. So when we walk on the path we mount shark:/autofs/export1 "on
+top of this mount point". Since these are always directories we can
+use the follow_link inode operation to trigger the mount.
+
+But, each entry in direct and indirect maps can have offsets (making
+them multi-mount map entries).
+
+For example, an indirect mount map entry could also be:
+
+g1  \
+   /        shark:/autofs/export5/testing/test \
+   /s1      shark:/autofs/export/testing/test/s1 \
+   /s2      shark:/autofs/export5/testing/test/s2 \
+   /s1/ss1  shark:/autofs/export1 \
+   /s2/ss2  shark:/autofs/export2
+
+and a similarly a direct mount map entry could also be:
+
+/automount/dparse/g1 \
+    /       shark:/autofs/export5/testing/test \
+    /s1     shark:/autofs/export/testing/test/s1 \
+    /s2     shark:/autofs/export5/testing/test/s2 \
+    /s1/ss1 shark:/autofs/export2 \
+    /s2/ss2 shark:/autofs/export2
+
+One of the issues with version 4 of autofs was that, when mounting an
+entry with a large number of offsets, possibly with nesting, we needed
+to mount and umount all of the offsets as a single unit. Not really a
+problem, except for people with a large number of offsets in map entries.
+This mechanism is used for the well known "hosts" map and we have seen
+cases (in 2.4) where the available number of mounts are exhausted or
+where the number of privileged ports available is exhausted.
+
+In version 5 we mount only as we go down the tree of offsets and
+similarly for expiring them which resolves the above problem. There is
+somewhat more detail to the implementation but it isn't needed for the
+sake of the problem explanation. The one important detail is that these
+offsets are implemented using the same mechanism as the direct mounts
+above and so the mount points can be covered by a mount.
+
+The current autofs implementation uses an ioctl file descriptor opened
+on the mount point for control operations. The references held by the
+descriptor are accounted for in checks made to determine if a mount is
+in use and is also used to access autofs file system information held
+in the mount super block. So the use of a file handle needs to be
+retained.
+
+
+The Solution
+============
+
+To be able to restart autofs leaving existing direct, indirect and
+offset mounts in place we need to be able to obtain a file handle
+for these potentially covered autofs mount points. Rather than just
+implement an isolated operation it was decided to re-implement the
+existing ioctl interface and add new operations to provide this
+functionality.
+
+In addition, to be able to reconstruct a mount tree that has busy mounts,
+the uid and gid of the last user that triggered the mount needs to be
+available because these can be used as macro substitution variables in
+autofs maps. They are recorded at mount request time and an operation
+has been added to retrieve them.
+
+Since we're re-implementing the control interface, a couple of other
+problems with the existing interface have been addressed. First, when
+a mount or expire operation completes a status is returned to the
+kernel by either a "send ready" or a "send fail" operation. The
+"send fail" operation of the ioctl interface could only ever send
+ENOENT so the re-implementation allows user space to send an actual
+status. Another expensive operation in user space, for those using
+very large maps, is discovering if a mount is present. Usually this
+involves scanning /proc/mounts and since it needs to be done quite
+often it can introduce significant overhead when there are many entries
+in the mount table. An operation to lookup the mount status of a mount
+point dentry (covered or not) has also been added.
+
+Current kernel development policy recommends avoiding the use of the
+ioctl mechanism in favor of systems such as Netlink. An implementation
+using this system was attempted to evaluate its suitability and it was
+found to be inadequate, in this case. The Generic Netlink system was
+used for this as raw Netlink would lead to a significant increase in
+complexity. There's no question that the Generic Netlink system is an
+elegant solution for common case ioctl functions but it's not a complete
+replacement probably because its primary purpose in life is to be a
+message bus implementation rather than specifically an ioctl replacement.
+While it would be possible to work around this there is one concern
+that lead to the decision to not use it. This is that the autofs
+expire in the daemon has become far to complex because umount
+candidates are enumerated, almost for no other reason than to "count"
+the number of times to call the expire ioctl. This involves scanning
+the mount table which has proved to be a big overhead for users with
+large maps. The best way to improve this is try and get back to the
+way the expire was done long ago. That is, when an expire request is
+issued for a mount (file handle) we should continually call back to
+the daemon until we can't umount any more mounts, then return the
+appropriate status to the daemon. At the moment we just expire one
+mount at a time. A Generic Netlink implementation would exclude this
+possibility for future development due to the requirements of the
+message bus architecture.
+
+
+autofs Miscellaneous Device mount control interface
+====================================================
+
+The control interface is opening a device node, typically /dev/autofs.
+
+All the ioctls use a common structure to pass the needed parameter
+information and return operation results:
+
+struct autofs_dev_ioctl {
+       __u32 ver_major;
+       __u32 ver_minor;
+       __u32 size;             /* total size of data passed in
+                                * including this struct */
+       __s32 ioctlfd;          /* automount command fd */
+
+       /* Command parameters */
+       union {
+               struct args_protover            protover;
+               struct args_protosubver         protosubver;
+               struct args_openmount           openmount;
+               struct args_ready               ready;
+               struct args_fail                fail;
+               struct args_setpipefd           setpipefd;
+               struct args_timeout             timeout;
+               struct args_requester           requester;
+               struct args_expire              expire;
+               struct args_askumount           askumount;
+               struct args_ismountpoint        ismountpoint;
+       };
+
+       char path[0];
+};
+
+The ioctlfd field is a mount point file descriptor of an autofs mount
+point. It is returned by the open call and is used by all calls except
+the check for whether a given path is a mount point, where it may
+optionally be used to check a specific mount corresponding to a given
+mount point file descriptor, and when requesting the uid and gid of the
+last successful mount on a directory within the autofs file system.
+
+The union is used to communicate parameters and results of calls made
+as described below.
+
+The path field is used to pass a path where it is needed and the size field
+is used account for the increased structure length when translating the
+structure sent from user space.
+
+This structure can be initialized before setting specific fields by using
+the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *).
+
+All of the ioctls perform a copy of this structure from user space to
+kernel space and return -EINVAL if the size parameter is smaller than
+the structure size itself, -ENOMEM if the kernel memory allocation fails
+or -EFAULT if the copy itself fails. Other checks include a version check
+of the compiled in user space version against the module version and a
+mismatch results in a -EINVAL return. If the size field is greater than
+the structure size then a path is assumed to be present and is checked to
+ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is
+returned. Following these checks, for all ioctl commands except
+AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and
+AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is
+not a valid descriptor or doesn't correspond to an autofs mount point
+an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is
+returned.
+
+
+The ioctls
+==========
+
+An example of an implementation which uses this interface can be seen
+in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the
+distribution tar available for download from kernel.org in directory
+/pub/linux/daemons/autofs/v5.
+
+The device node ioctl operations implemented by this interface are:
+
+
+AUTOFS_DEV_IOCTL_VERSION
+------------------------
+
+Get the major and minor version of the autofs device ioctl kernel module
+implementation. It requires an initialized struct autofs_dev_ioctl as an
+input parameter and sets the version information in the passed in structure.
+It returns 0 on success or the error -EINVAL if a version mismatch is
+detected.
+
+
+AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD
+------------------------------------------------------------------
+
+Get the major and minor version of the autofs protocol version understood
+by loaded module. This call requires an initialized struct autofs_dev_ioctl
+with the ioctlfd field set to a valid autofs mount point descriptor
+and sets the requested version number in version field of struct args_protover
+or sub_version field of struct args_protosubver. These commands return
+0 on success or one of the negative error codes if validation fails.
+
+
+AUTOFS_DEV_IOCTL_OPENMOUNT and AUTOFS_DEV_IOCTL_CLOSEMOUNT
+----------------------------------------------------------
+
+Obtain and release a file descriptor for an autofs managed mount point
+path. The open call requires an initialized struct autofs_dev_ioctl with
+the path field set and the size field adjusted appropriately as well
+as the devid field of struct args_openmount set to the device number of
+the autofs mount. The device number can be obtained from the mount options
+shown in /proc/mounts. The close call requires an initialized struct
+autofs_dev_ioct with the ioctlfd field set to the descriptor obtained
+from the open call. The release of the file descriptor can also be done
+with close(2) so any open descriptors will also be closed at process exit.
+The close call is included in the implemented operations largely for
+completeness and to provide for a consistent user space implementation.
+
+
+AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD
+--------------------------------------------------------
+
+Return mount and expire result status from user space to the kernel.
+Both of these calls require an initialized struct autofs_dev_ioctl
+with the ioctlfd field set to the descriptor obtained from the open
+call and the token field of struct args_ready or struct args_fail set
+to the wait queue token number, received by user space in the foregoing
+mount or expire request. The status field of struct args_fail is set to
+the errno of the operation. It is set to 0 on success.
+
+
+AUTOFS_DEV_IOCTL_SETPIPEFD_CMD
+------------------------------
+
+Set the pipe file descriptor used for kernel communication to the daemon.
+Normally this is set at mount time using an option but when reconnecting
+to a existing mount we need to use this to tell the autofs mount about
+the new kernel pipe descriptor. In order to protect mounts against
+incorrectly setting the pipe descriptor we also require that the autofs
+mount be catatonic (see next call).
+
+The call requires an initialized struct autofs_dev_ioctl with the
+ioctlfd field set to the descriptor obtained from the open call and
+the pipefd field of struct args_setpipefd set to descriptor of the pipe.
+On success the call also sets the process group id used to identify the
+controlling process (eg. the owning automount(8) daemon) to the process
+group of the caller.
+
+
+AUTOFS_DEV_IOCTL_CATATONIC_CMD
+------------------------------
+
+Make the autofs mount point catatonic. The autofs mount will no longer
+issue mount requests, the kernel communication pipe descriptor is released
+and any remaining waits in the queue released.
+
+The call requires an initialized struct autofs_dev_ioctl with the
+ioctlfd field set to the descriptor obtained from the open call.
+
+
+AUTOFS_DEV_IOCTL_TIMEOUT_CMD
+----------------------------
+
+Set the expire timeout for mounts within an autofs mount point.
+
+The call requires an initialized struct autofs_dev_ioctl with the
+ioctlfd field set to the descriptor obtained from the open call.
+
+
+AUTOFS_DEV_IOCTL_REQUESTER_CMD
+------------------------------
+
+Return the uid and gid of the last process to successfully trigger a the
+mount on the given path dentry.
+
+The call requires an initialized struct autofs_dev_ioctl with the path
+field set to the mount point in question and the size field adjusted
+appropriately. Upon return the uid field of struct args_requester contains
+the uid and gid field the gid.
+
+When reconstructing an autofs mount tree with active mounts we need to
+re-connect to mounts that may have used the original process uid and
+gid (or string variations of them) for mount lookups within the map entry.
+This call provides the ability to obtain this uid and gid so they may be
+used by user space for the mount map lookups.
+
+
+AUTOFS_DEV_IOCTL_EXPIRE_CMD
+---------------------------
+
+Issue an expire request to the kernel for an autofs mount. Typically
+this ioctl is called until no further expire candidates are found.
+
+The call requires an initialized struct autofs_dev_ioctl with the
+ioctlfd field set to the descriptor obtained from the open call. In
+addition an immediate expire, independent of the mount timeout, can be
+requested by setting the how field of struct args_expire to 1. If no
+expire candidates can be found the ioctl returns -1 with errno set to
+EAGAIN.
+
+This call causes the kernel module to check the mount corresponding
+to the given ioctlfd for mounts that can be expired, issues an expire
+request back to the daemon and waits for completion.
+
+AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD
+------------------------------
+
+Checks if an autofs mount point is in use.
+
+The call requires an initialized struct autofs_dev_ioctl with the
+ioctlfd field set to the descriptor obtained from the open call and
+it returns the result in the may_umount field of struct args_askumount,
+1 for busy and 0 otherwise.
+
+
+AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD
+---------------------------------
+
+Check if the given path is a mountpoint.
+
+The call requires an initialized struct autofs_dev_ioctl. There are two
+possible variations. Both use the path field set to the path of the mount
+point to check and the size field adjusted appropriately. One uses the
+ioctlfd field to identify a specific mount point to check while the other
+variation uses the path and optionally in.type field of struct args_ismountpoint
+set to an autofs mount type. The call returns 1 if this is a mount point
+and sets out.devid field to the device number of the mount and out.magic
+field to the relevant super block magic number (described below) or 0 if
+it isn't a mountpoint. In both cases the the device number (as returned
+by new_encode_dev()) is returned in out.devid field.
+
+If supplied with a file descriptor we're looking for a specific mount,
+not necessarily at the top of the mounted stack. In this case the path
+the descriptor corresponds to is considered a mountpoint if it is itself
+a mountpoint or contains a mount, such as a multi-mount without a root
+mount. In this case we return 1 if the descriptor corresponds to a mount
+point and and also returns the super magic of the covering mount if there
+is one or 0 if it isn't a mountpoint.
+
+If a path is supplied (and the ioctlfd field is set to -1) then the path
+is looked up and is checked to see if it is the root of a mount. If a
+type is also given we are looking for a particular autofs mount and if
+a match isn't found a fail is returned. If the the located path is the
+root of a mount 1 is returned along with the super magic of the mount
+or 0 otherwise.
diff --git a/Documentation/filesystems/autofs.txt b/Documentation/filesystems/autofs.txt
new file mode 100644 (file)
index 0000000..373ad25
--- /dev/null
@@ -0,0 +1,529 @@
+<head>
+<style> p { max-width:50em} ol, ul {max-width: 40em}</style>
+</head>
+
+autofs - how it works
+=====================
+
+Purpose
+-------
+
+The goal of autofs is to provide on-demand mounting and race free
+automatic unmounting of various other filesystems.  This provides two
+key advantages:
+
+1. There is no need to delay boot until all filesystems that
+   might be needed are mounted.  Processes that try to access those
+   slow filesystems might be delayed but other processes can
+   continue freely.  This is particularly important for
+   network filesystems (e.g. NFS) or filesystems stored on
+   media with a media-changing robot.
+
+2. The names and locations of filesystems can be stored in
+   a remote database and can change at any time.  The content
+   in that data base at the time of access will be used to provide
+   a target for the access.  The interpretation of names in the
+   filesystem can even be programmatic rather than database-backed,
+   allowing wildcards for example, and can vary based on the user who
+   first accessed a name.
+
+Context
+-------
+
+The "autofs" filesystem module is only one part of an autofs system.
+There also needs to be a user-space program which looks up names
+and mounts filesystems.  This will often be the "automount" program,
+though other tools including "systemd" can make use of "autofs".
+This document describes only the kernel module and the interactions
+required with any user-space program.  Subsequent text refers to this
+as the "automount daemon" or simply "the daemon".
+
+"autofs" is a Linux kernel module with provides the "autofs"
+filesystem type.  Several "autofs" filesystems can be mounted and they
+can each be managed separately, or all managed by the same daemon.
+
+Content
+-------
+
+An autofs filesystem can contain 3 sorts of objects: directories,
+symbolic links and mount traps.  Mount traps are directories with
+extra properties as described in the next section.
+
+Objects can only be created by the automount daemon: symlinks are
+created with a regular `symlink` system call, while directories and
+mount traps are created with `mkdir`.  The determination of whether a
+directory should be a mount trap or not is quite _ad hoc_, largely for
+historical reasons, and is determined in part by the
+*direct*/*indirect*/*offset* mount options, and the *maxproto* mount option.
+
+If neither the *direct* or *offset* mount options are given (so the
+mount is considered to be *indirect*), then the root directory is
+always a regular directory, otherwise it is a mount trap when it is
+empty and a regular directory when not empty.  Note that *direct* and
+*offset* are treated identically so a concise summary is that the root
+directory is a mount trap only if the filesystem is mounted *direct*
+and the root is empty.
+
+Directories created in the root directory are mount traps only if the
+filesystem is mounted *indirect* and they are empty.
+
+Directories further down the tree depend on the *maxproto* mount
+option and particularly whether it is less than five or not.
+When *maxproto* is five, no directories further down the
+tree are ever mount traps, they are always regular directories.  When
+the *maxproto* is four (or three), these directories are mount traps
+precisely when they are empty.
+
+So: non-empty (i.e. non-leaf) directories are never mount traps. Empty
+directories are sometimes mount traps, and sometimes not depending on
+where in the tree they are (root, top level, or lower), the *maxproto*,
+and whether the mount was *indirect* or not.
+
+Mount Traps
+---------------
+
+A core element of the implementation of autofs is the Mount Traps
+which are provided by the Linux VFS.  Any directory provided by a
+filesystem can be designated as a trap.  This involves two separate
+features that work together to allow autofs to do its job.
+
+**DCACHE_NEED_AUTOMOUNT**
+
+If a dentry has the DCACHE_NEED_AUTOMOUNT flag set (which gets set if
+the inode has S_AUTOMOUNT set, or can be set directly) then it is
+(potentially) a mount trap.  Any access to this directory beyond a
+"`stat`" will (normally) cause the `d_op->d_automount()` dentry operation
+to be called. The task of this method is to find the filesystem that
+should be mounted on the directory and to return it.  The VFS is
+responsible for actually mounting the root of this filesystem on the
+directory.
+
+autofs doesn't find the filesystem itself but sends a message to the
+automount daemon asking it to find and mount the filesystem.  The
+autofs `d_automount` method then waits for the daemon to report that
+everything is ready.  It will then return "`NULL`" indicating that the
+mount has already happened.  The VFS doesn't try to mount anything but
+follows down the mount that is already there.
+
+This functionality is sufficient for some users of mount traps such
+as NFS which creates traps so that mountpoints on the server can be
+reflected on the client.  However it is not sufficient for autofs.  As
+mounting onto a directory is considered to be "beyond a `stat`", the
+automount daemon would not be able to mount a filesystem on the 'trap'
+directory without some way to avoid getting caught in the trap.  For
+that purpose there is another flag.
+
+**DCACHE_MANAGE_TRANSIT**
+
+If a dentry has DCACHE_MANAGE_TRANSIT set then two very different but
+related behaviors are invoked, both using the `d_op->d_manage()`
+dentry operation.
+
+Firstly, before checking to see if any filesystem is mounted on the
+directory, d_manage() will be called with the `rcu_walk` parameter set
+to `false`.  It may return one of three things:
+
+-  A return value of zero indicates that there is nothing special
+   about this dentry and normal checks for mounts and automounts
+   should proceed.
+
+   autofs normally returns zero, but first waits for any
+   expiry (automatic unmounting of the mounted filesystem) to
+   complete.  This avoids races.
+
+-  A return value of `-EISDIR` tells the VFS to ignore any mounts
+   on the directory and to not consider calling `->d_automount()`.
+   This effectively disables the **DCACHE_NEED_AUTOMOUNT** flag
+   causing the directory not be a mount trap after all.
+
+   autofs returns this if it detects that the process performing the
+   lookup is the automount daemon and that the mount has been
+   requested but has not yet completed.  How it determines this is
+   discussed later.  This allows the automount daemon not to get
+   caught in the mount trap.
+
+   There is a subtlety here.  It is possible that a second autofs
+   filesystem can be mounted below the first and for both of them to
+   be managed by the same daemon.  For the daemon to be able to mount
+   something on the second it must be able to "walk" down past the
+   first.  This means that d_manage cannot *always* return -EISDIR for
+   the automount daemon.  It must only return it when a mount has
+   been requested, but has not yet completed.
+
+   `d_manage` also returns `-EISDIR` if the dentry shouldn't be a
+   mount trap, either because it is a symbolic link or because it is
+   not empty.
+
+-  Any other negative value is treated as an error and returned
+   to the caller.
+
+   autofs can return
+
+   - -ENOENT if the automount daemon failed to mount anything,
+   - -ENOMEM if it ran out of memory,
+   - -EINTR if a signal arrived while waiting for expiry to
+     complete
+   - or any other error sent down by the automount daemon.
+
+
+The second use case only occurs during an "RCU-walk" and so `rcu_walk`
+will be set.
+
+An RCU-walk is a fast and lightweight process for walking down a
+filename path (i.e. it is like running on tip-toes).  RCU-walk cannot
+cope with all situations so when it finds a difficulty it falls back
+to "REF-walk", which is slower but more robust.
+
+RCU-walk will never call `->d_automount`; the filesystems must already
+be mounted or RCU-walk cannot handle the path.
+To determine if a mount-trap is safe for RCU-walk mode it calls
+`->d_manage()` with `rcu_walk` set to `true`.
+
+In this case `d_manage()` must avoid blocking and should avoid taking
+spinlocks if at all possible.  Its sole purpose is to determine if it
+would be safe to follow down into any mounted directory and the only
+reason that it might not be is if an expiry of the mount is
+underway.
+
+In the `rcu_walk` case, `d_manage()` cannot return -EISDIR to tell the
+VFS that this is a directory that doesn't require d_automount.  If
+`rcu_walk` sees a dentry with DCACHE_NEED_AUTOMOUNT set but nothing
+mounted, it *will* fall back to REF-walk.  `d_manage()` cannot make the
+VFS remain in RCU-walk mode, but can only tell it to get out of
+RCU-walk mode by returning `-ECHILD`.
+
+So `d_manage()`, when called with `rcu_walk` set, should either return
+-ECHILD if there is any reason to believe it is unsafe to end the
+mounted filesystem, and otherwise should return 0.
+
+autofs will return `-ECHILD` if an expiry of the filesystem has been
+initiated or is being considered, otherwise it returns 0.
+
+
+Mountpoint expiry
+-----------------
+
+The VFS has a mechanism for automatically expiring unused mounts,
+much as it can expire any unused dentry information from the dcache.
+This is guided by the MNT_SHRINKABLE flag.  This only applies to
+mounts that were created by `d_automount()` returning a filesystem to be
+mounted.  As autofs doesn't return such a filesystem but leaves the
+mounting to the automount daemon, it must involve the automount daemon
+in unmounting as well.  This also means that autofs has more control
+of expiry.
+
+The VFS also supports "expiry" of mounts using the MNT_EXPIRE flag to
+the `umount` system call.  Unmounting with MNT_EXPIRE will fail unless
+a previous attempt had been made, and the filesystem has been inactive
+and untouched since that previous attempt.  autofs does not depend on
+this but has its own internal tracking of whether filesystems were
+recently used.  This allows individual names in the autofs directory
+to expire separately.
+
+With version 4 of the protocol, the automount daemon can try to
+unmount any filesystems mounted on the autofs filesystem or remove any
+symbolic links or empty directories any time it likes.  If the unmount
+or removal is successful the filesystem will be returned to the state
+it was before the mount or creation, so that any access of the name
+will trigger normal auto-mount processing.  In particlar, `rmdir` and
+`unlink` do not leave negative entries in the dcache as a normal
+filesystem would, so an attempt to access a recently-removed object is
+passed to autofs for handling.
+
+With version 5, this is not safe except for unmounting from top-level
+directories.  As lower-level directories are never mount traps, other
+processes will see an empty directory as soon as the filesystem is
+unmounted.  So it is generally safest to use the autofs expiry
+protocol described below.
+
+Normally the daemon only wants to remove entries which haven't been
+used for a while.  For this purpose autofs maintains a "`last_used`"
+time stamp on each directory or symlink.  For symlinks it genuinely
+does record the last time the symlink was "used" or followed to find
+out where it points to.  For directories the field is a slight
+misnomer.  It actually records the last time that autofs checked if
+the directory or one of its descendents was busy and found that it
+was.  This is just as useful and doesn't require updating the field so
+often.
+
+The daemon is able to ask autofs if anything is due to be expired,
+using an `ioctl` as discussed later.  For a *direct* mount, autofs
+considers if the entire mount-tree can be unmounted or not.  For an
+*indirect* mount, autofs considers each of the names in the top level
+directory to determine if any of those can be unmounted and cleaned
+up.
+
+There is an option with indirect mounts to consider each of the leaves
+that has been mounted on instead of considering the top-level names.
+This is intended for compatability with version 4 of autofs and should
+be considered as deprecated.
+
+When autofs considers a directory it checks the `last_used` time and
+compares it with the "timeout" value set when the filesystem was
+mounted, though this check is ignored in some cases. It also checks if
+the directory or anything below it is in use.  For symbolic links,
+only the `last_used` time is ever considered.
+
+If both appear to support expiring the directory or symlink, an action
+is taken.
+
+There are two ways to ask autofs to consider expiry.  The first is to
+use the **AUTOFS_IOC_EXPIRE** ioctl.  This only works for indirect
+mounts.  If it finds something in the root directory to expire it will
+return the name of that thing.  Once a name has been returned the
+automount daemon needs to unmount any filesystems mounted below the
+name normally.  As described above, this is unsafe for non-toplevel
+mounts in a version-5 autofs.  For this reason the current `automountd`
+does not use this ioctl.
+
+The second mechanism uses either the **AUTOFS_DEV_IOCTL_EXPIRE_CMD** or
+the **AUTOFS_IOC_EXPIRE_MULTI** ioctl.  This will work for both direct and
+indirect mounts.  If it selects an object to expire, it will notify
+the daemon using the notification mechanism described below.  This
+will block until the daemon acknowledges the expiry notification.
+This implies that the "`EXPIRE`" ioctl must be sent from a different
+thread than the one which handles notification.
+
+While the ioctl is blocking, the entry is marked as "expiring" and
+`d_manage` will block until the daemon affirms that the unmount has
+completed (together with removing any directories that might have been
+necessary), or has been aborted.
+
+Communicating with autofs: detecting the daemon
+-----------------------------------------------
+
+There are several forms of communication between the automount daemon
+and the filesystem.  As we have already seen, the daemon can create and
+remove directories and symlinks using normal filesystem operations.
+autofs knows whether a process requesting some operation is the daemon
+or not based on its process-group id number (see getpgid(1)).
+
+When an autofs filesystem is mounted the pgid of the mounting
+processes is recorded unless the "pgrp=" option is given, in which
+case that number is recorded instead.  Any request arriving from a
+process in that process group is considered to come from the daemon.
+If the daemon ever has to be stopped and restarted a new pgid can be
+provided through an ioctl as will be described below.
+
+Communicating with autofs: the event pipe
+-----------------------------------------
+
+When an autofs filesystem is mounted, the 'write' end of a pipe must
+be passed using the 'fd=' mount option.  autofs will write
+notification messages to this pipe for the daemon to respond to.
+For version 5, the format of the message is:
+
+        struct autofs_v5_packet {
+                int proto_version;                /* Protocol version */
+                int type;                        /* Type of packet */
+                autofs_wqt_t wait_queue_token;
+                __u32 dev;
+                __u64 ino;
+                __u32 uid;
+                __u32 gid;
+                __u32 pid;
+                __u32 tgid;
+                __u32 len;
+                char name[NAME_MAX+1];
+        };
+
+where the type is one of
+
+        autofs_ptype_missing_indirect
+        autofs_ptype_expire_indirect
+        autofs_ptype_missing_direct
+        autofs_ptype_expire_direct
+
+so messages can indicate that a name is missing (something tried to
+access it but it isn't there) or that it has been selected for expiry.
+
+The pipe will be set to "packet mode" (equivalent to passing
+`O_DIRECT`) to _pipe2(2)_ so that a read from the pipe will return at
+most one packet, and any unread portion of a packet will be discarded.
+
+The `wait_queue_token` is a unique number which can identify a
+particular request to be acknowledged.  When a message is sent over
+the pipe the affected dentry is marked as either "active" or
+"expiring" and other accesses to it block until the message is
+acknowledged using one of the ioctls below and the relevant
+`wait_queue_token`.
+
+Communicating with autofs: root directory ioctls
+------------------------------------------------
+
+The root directory of an autofs filesystem will respond to a number of
+ioctls.  The process issuing the ioctl must have the CAP_SYS_ADMIN
+capability, or must be the automount daemon.
+
+The available ioctl commands are:
+
+- **AUTOFS_IOC_READY**: a notification has been handled.  The argument
+    to the ioctl command is the "wait_queue_token" number
+    corresponding to the notification being acknowledged.
+- **AUTOFS_IOC_FAIL**: similar to above, but indicates failure with
+    the error code `ENOENT`.
+- **AUTOFS_IOC_CATATONIC**: Causes the autofs to enter "catatonic"
+    mode meaning that it stops sending notifications to the daemon.
+    This mode is also entered if a write to the pipe fails.
+- **AUTOFS_IOC_PROTOVER**:  This returns the protocol version in use.
+- **AUTOFS_IOC_PROTOSUBVER**: Returns the protocol sub-version which
+    is really a version number for the implementation.  It is
+    currently 2.
+- **AUTOFS_IOC_SETTIMEOUT**:  This passes a pointer to an unsigned
+    long.  The value is used to set the timeout for expiry, and
+    the current timeout value is stored back through the pointer.
+- **AUTOFS_IOC_ASKUMOUNT**:  Returns, in the pointed-to `int`, 1 if
+    the filesystem could be unmounted.  This is only a hint as
+    the situation could change at any instant.  This call can be
+    use to avoid a more expensive full unmount attempt.
+- **AUTOFS_IOC_EXPIRE**: as described above, this asks if there is
+    anything suitable to expire.  A pointer to a packet:
+
+        struct autofs_packet_expire_multi {
+                int proto_version;              /* Protocol version */
+                int type;                       /* Type of packet */
+                autofs_wqt_t wait_queue_token;
+                int len;
+                char name[NAME_MAX+1];
+        };
+
+     is required.  This is filled in with the name of something
+     that can be unmounted or removed.  If nothing can be expired,
+     `errno` is set to `EAGAIN`.  Even though a `wait_queue_token`
+     is present in the structure, no "wait queue" is established
+     and no acknowledgment is needed.
+- **AUTOFS_IOC_EXPIRE_MULTI**:  This is similar to
+     **AUTOFS_IOC_EXPIRE** except that it causes notification to be
+     sent to the daemon, and it blocks until the daemon acknowledges.
+     The argument is an integer which can contain two different flags.
+
+     **AUTOFS_EXP_IMMEDIATE** causes `last_used` time to be ignored
+     and objects are expired if the are not in use.
+
+     **AUTOFS_EXP_LEAVES** will select a leaf rather than a top-level
+     name to expire.  This is only safe when *maxproto* is 4.
+
+Communicating with autofs: char-device ioctls
+---------------------------------------------
+
+It is not always possible to open the root of an autofs filesystem,
+particularly a *direct* mounted filesystem.  If the automount daemon
+is restarted there is no way for it to regain control of existing
+mounts using any of the above communication channels.  To address this
+need there is a "miscellaneous" character device (major 10, minor 235)
+which can be used to communicate directly with the autofs filesystem.
+It requires CAP_SYS_ADMIN for access.
+
+The `ioctl`s that can be used on this device are described in a separate
+document `autofs-mount-control.txt`, and are summarized briefly here.
+Each ioctl is passed a pointer to an `autofs_dev_ioctl` structure:
+
+        struct autofs_dev_ioctl {
+                __u32 ver_major;
+                __u32 ver_minor;
+                __u32 size;             /* total size of data passed in
+                                         * including this struct */
+                __s32 ioctlfd;          /* automount command fd */
+
+               /* Command parameters */
+               union {
+                       struct args_protover            protover;
+                       struct args_protosubver         protosubver;
+                       struct args_openmount           openmount;
+                       struct args_ready               ready;
+                       struct args_fail                fail;
+                       struct args_setpipefd           setpipefd;
+                       struct args_timeout             timeout;
+                       struct args_requester           requester;
+                       struct args_expire              expire;
+                       struct args_askumount           askumount;
+                       struct args_ismountpoint        ismountpoint;
+               };
+
+                char path[0];
+        };
+
+For the **OPEN_MOUNT** and **IS_MOUNTPOINT** commands, the target
+filesystem is identified by the `path`.  All other commands identify
+the filesystem by the `ioctlfd` which is a file descriptor open on the
+root, and which can be returned by **OPEN_MOUNT**.
+
+The `ver_major` and `ver_minor` are in/out parameters which check that
+the requested version is supported, and report the maximum version
+that the kernel module can support.
+
+Commands are:
+
+- **AUTOFS_DEV_IOCTL_VERSION_CMD**: does nothing, except validate and
+    set version numbers.
+- **AUTOFS_DEV_IOCTL_OPENMOUNT_CMD**: return an open file descriptor
+    on the root of an autofs filesystem.  The filesystem is identified
+    by name and device number, which is stored in `openmount.devid`.
+    Device numbers for existing filesystems can be found in
+    `/proc/self/mountinfo`.
+- **AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD**: same as `close(ioctlfd)`.
+- **AUTOFS_DEV_IOCTL_SETPIPEFD_CMD**: if the filesystem is in
+    catatonic mode, this can provide the write end of a new pipe
+    in `setpipefd.pipefd` to re-establish communication with a daemon.
+    The process group of the calling process is used to identify the
+    daemon.
+- **AUTOFS_DEV_IOCTL_REQUESTER_CMD**: `path` should be a
+    name within the filesystem that has been auto-mounted on.
+    On successful return, `requester.uid` and `requester.gid` will be
+    the UID and GID of the process which triggered that mount.
+- **AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD**: Check if path is a
+    mountpoint of a particular type - see separate documentation for
+    details.
+- **AUTOFS_DEV_IOCTL_PROTOVER_CMD**:
+- **AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD**:
+- **AUTOFS_DEV_IOCTL_READY_CMD**:
+- **AUTOFS_DEV_IOCTL_FAIL_CMD**:
+- **AUTOFS_DEV_IOCTL_CATATONIC_CMD**:
+- **AUTOFS_DEV_IOCTL_TIMEOUT_CMD**:
+- **AUTOFS_DEV_IOCTL_EXPIRE_CMD**:
+- **AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD**:  These all have the same
+    function as the similarly named **AUTOFS_IOC** ioctls, except
+    that **FAIL** can be given an explicit error number in `fail.status`
+    instead of assuming `ENOENT`, and this **EXPIRE** command
+    corresponds to **AUTOFS_IOC_EXPIRE_MULTI**.
+
+Catatonic mode
+--------------
+
+As mentioned, an autofs mount can enter "catatonic" mode.  This
+happens if a write to the notification pipe fails, or if it is
+explicitly requested by an `ioctl`.
+
+When entering catatonic mode, the pipe is closed and any pending
+notifications are acknowledged with the error `ENOENT`.
+
+Once in catatonic mode attempts to access non-existing names will
+result in `ENOENT` while attempts to access existing directories will
+be treated in the same way as if they came from the daemon, so mount
+traps will not fire.
+
+When the filesystem is mounted a _uid_ and _gid_ can be given which
+set the ownership of directories and symbolic links.  When the
+filesystem is in catatonic mode, any process with a matching UID can
+create directories or symlinks in the root directory, but not in other
+directories.
+
+Catatonic mode can only be left via the
+**AUTOFS_DEV_IOCTL_OPENMOUNT_CMD** ioctl on the `/dev/autofs`.
+
+autofs, name spaces, and shared mounts
+--------------------------------------
+
+With bind mounts and name spaces it is possible for an autofs
+filesystem to appear at multiple places in one or more filesystem
+name spaces.  For this to work sensibly, the autofs filesystem should
+always be mounted "shared". e.g.
+
+> `mount --make-shared /autofs/mount/point`
+
+The automount daemon is only able to manage a single mount location for
+an autofs filesystem and if mounts on that are not 'shared', other
+locations will not behave as expected.  In particular access to those
+other locations will likely result in the `ELOOP` error
+
+> Too many levels of symbolic links
diff --git a/Documentation/filesystems/autofs4-mount-control.txt b/Documentation/filesystems/autofs4-mount-control.txt
deleted file mode 100644 (file)
index e5177cb..0000000
+++ /dev/null
@@ -1,407 +0,0 @@
-
-Miscellaneous Device control operations for the autofs4 kernel module
-====================================================================
-
-The problem
-===========
-
-There is a problem with active restarts in autofs (that is to say
-restarting autofs when there are busy mounts).
-
-During normal operation autofs uses a file descriptor opened on the
-directory that is being managed in order to be able to issue control
-operations. Using a file descriptor gives ioctl operations access to
-autofs specific information stored in the super block. The operations
-are things such as setting an autofs mount catatonic, setting the
-expire timeout and requesting expire checks. As is explained below,
-certain types of autofs triggered mounts can end up covering an autofs
-mount itself which prevents us being able to use open(2) to obtain a
-file descriptor for these operations if we don't already have one open.
-
-Currently autofs uses "umount -l" (lazy umount) to clear active mounts
-at restart. While using lazy umount works for most cases, anything that
-needs to walk back up the mount tree to construct a path, such as
-getcwd(2) and the proc file system /proc/<pid>/cwd, no longer works
-because the point from which the path is constructed has been detached
-from the mount tree.
-
-The actual problem with autofs is that it can't reconnect to existing
-mounts. Immediately one thinks of just adding the ability to remount
-autofs file systems would solve it, but alas, that can't work. This is
-because autofs direct mounts and the implementation of "on demand mount
-and expire" of nested mount trees have the file system mounted directly
-on top of the mount trigger directory dentry.
-
-For example, there are two types of automount maps, direct (in the kernel
-module source you will see a third type called an offset, which is just
-a direct mount in disguise) and indirect.
-
-Here is a master map with direct and indirect map entries:
-
-/-      /etc/auto.direct
-/test   /etc/auto.indirect
-
-and the corresponding map files:
-
-/etc/auto.direct:
-
-/automount/dparse/g6  budgie:/autofs/export1
-/automount/dparse/g1  shark:/autofs/export1
-and so on.
-
-/etc/auto.indirect:
-
-g1    shark:/autofs/export1
-g6    budgie:/autofs/export1
-and so on.
-
-For the above indirect map an autofs file system is mounted on /test and
-mounts are triggered for each sub-directory key by the inode lookup
-operation. So we see a mount of shark:/autofs/export1 on /test/g1, for
-example.
-
-The way that direct mounts are handled is by making an autofs mount on
-each full path, such as /automount/dparse/g1, and using it as a mount
-trigger. So when we walk on the path we mount shark:/autofs/export1 "on
-top of this mount point". Since these are always directories we can
-use the follow_link inode operation to trigger the mount.
-
-But, each entry in direct and indirect maps can have offsets (making
-them multi-mount map entries).
-
-For example, an indirect mount map entry could also be:
-
-g1  \
-   /        shark:/autofs/export5/testing/test \
-   /s1      shark:/autofs/export/testing/test/s1 \
-   /s2      shark:/autofs/export5/testing/test/s2 \
-   /s1/ss1  shark:/autofs/export1 \
-   /s2/ss2  shark:/autofs/export2
-
-and a similarly a direct mount map entry could also be:
-
-/automount/dparse/g1 \
-    /       shark:/autofs/export5/testing/test \
-    /s1     shark:/autofs/export/testing/test/s1 \
-    /s2     shark:/autofs/export5/testing/test/s2 \
-    /s1/ss1 shark:/autofs/export2 \
-    /s2/ss2 shark:/autofs/export2
-
-One of the issues with version 4 of autofs was that, when mounting an
-entry with a large number of offsets, possibly with nesting, we needed
-to mount and umount all of the offsets as a single unit. Not really a
-problem, except for people with a large number of offsets in map entries.
-This mechanism is used for the well known "hosts" map and we have seen
-cases (in 2.4) where the available number of mounts are exhausted or
-where the number of privileged ports available is exhausted.
-
-In version 5 we mount only as we go down the tree of offsets and
-similarly for expiring them which resolves the above problem. There is
-somewhat more detail to the implementation but it isn't needed for the
-sake of the problem explanation. The one important detail is that these
-offsets are implemented using the same mechanism as the direct mounts
-above and so the mount points can be covered by a mount.
-
-The current autofs implementation uses an ioctl file descriptor opened
-on the mount point for control operations. The references held by the
-descriptor are accounted for in checks made to determine if a mount is
-in use and is also used to access autofs file system information held
-in the mount super block. So the use of a file handle needs to be
-retained.
-
-
-The Solution
-============
-
-To be able to restart autofs leaving existing direct, indirect and
-offset mounts in place we need to be able to obtain a file handle
-for these potentially covered autofs mount points. Rather than just
-implement an isolated operation it was decided to re-implement the
-existing ioctl interface and add new operations to provide this
-functionality.
-
-In addition, to be able to reconstruct a mount tree that has busy mounts,
-the uid and gid of the last user that triggered the mount needs to be
-available because these can be used as macro substitution variables in
-autofs maps. They are recorded at mount request time and an operation
-has been added to retrieve them.
-
-Since we're re-implementing the control interface, a couple of other
-problems with the existing interface have been addressed. First, when
-a mount or expire operation completes a status is returned to the
-kernel by either a "send ready" or a "send fail" operation. The
-"send fail" operation of the ioctl interface could only ever send
-ENOENT so the re-implementation allows user space to send an actual
-status. Another expensive operation in user space, for those using
-very large maps, is discovering if a mount is present. Usually this
-involves scanning /proc/mounts and since it needs to be done quite
-often it can introduce significant overhead when there are many entries
-in the mount table. An operation to lookup the mount status of a mount
-point dentry (covered or not) has also been added.
-
-Current kernel development policy recommends avoiding the use of the
-ioctl mechanism in favor of systems such as Netlink. An implementation
-using this system was attempted to evaluate its suitability and it was
-found to be inadequate, in this case. The Generic Netlink system was
-used for this as raw Netlink would lead to a significant increase in
-complexity. There's no question that the Generic Netlink system is an
-elegant solution for common case ioctl functions but it's not a complete
-replacement probably because its primary purpose in life is to be a
-message bus implementation rather than specifically an ioctl replacement.
-While it would be possible to work around this there is one concern
-that lead to the decision to not use it. This is that the autofs
-expire in the daemon has become far to complex because umount
-candidates are enumerated, almost for no other reason than to "count"
-the number of times to call the expire ioctl. This involves scanning
-the mount table which has proved to be a big overhead for users with
-large maps. The best way to improve this is try and get back to the
-way the expire was done long ago. That is, when an expire request is
-issued for a mount (file handle) we should continually call back to
-the daemon until we can't umount any more mounts, then return the
-appropriate status to the daemon. At the moment we just expire one
-mount at a time. A Generic Netlink implementation would exclude this
-possibility for future development due to the requirements of the
-message bus architecture.
-
-
-autofs4 Miscellaneous Device mount control interface
-====================================================
-
-The control interface is opening a device node, typically /dev/autofs.
-
-All the ioctls use a common structure to pass the needed parameter
-information and return operation results:
-
-struct autofs_dev_ioctl {
-       __u32 ver_major;
-       __u32 ver_minor;
-       __u32 size;             /* total size of data passed in
-                                * including this struct */
-       __s32 ioctlfd;          /* automount command fd */
-
-       /* Command parameters */
-       union {
-               struct args_protover            protover;
-               struct args_protosubver         protosubver;
-               struct args_openmount           openmount;
-               struct args_ready               ready;
-               struct args_fail                fail;
-               struct args_setpipefd           setpipefd;
-               struct args_timeout             timeout;
-               struct args_requester           requester;
-               struct args_expire              expire;
-               struct args_askumount           askumount;
-               struct args_ismountpoint        ismountpoint;
-       };
-
-       char path[0];
-};
-
-The ioctlfd field is a mount point file descriptor of an autofs mount
-point. It is returned by the open call and is used by all calls except
-the check for whether a given path is a mount point, where it may
-optionally be used to check a specific mount corresponding to a given
-mount point file descriptor, and when requesting the uid and gid of the
-last successful mount on a directory within the autofs file system.
-
-The union is used to communicate parameters and results of calls made
-as described below.
-
-The path field is used to pass a path where it is needed and the size field
-is used account for the increased structure length when translating the
-structure sent from user space.
-
-This structure can be initialized before setting specific fields by using
-the void function call init_autofs_dev_ioctl(struct autofs_dev_ioctl *).
-
-All of the ioctls perform a copy of this structure from user space to
-kernel space and return -EINVAL if the size parameter is smaller than
-the structure size itself, -ENOMEM if the kernel memory allocation fails
-or -EFAULT if the copy itself fails. Other checks include a version check
-of the compiled in user space version against the module version and a
-mismatch results in a -EINVAL return. If the size field is greater than
-the structure size then a path is assumed to be present and is checked to
-ensure it begins with a "/" and is NULL terminated, otherwise -EINVAL is
-returned. Following these checks, for all ioctl commands except
-AUTOFS_DEV_IOCTL_VERSION_CMD, AUTOFS_DEV_IOCTL_OPENMOUNT_CMD and
-AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD the ioctlfd is validated and if it is
-not a valid descriptor or doesn't correspond to an autofs mount point
-an error of -EBADF, -ENOTTY or -EINVAL (not an autofs descriptor) is
-returned.
-
-
-The ioctls
-==========
-
-An example of an implementation which uses this interface can be seen
-in autofs version 5.0.4 and later in file lib/dev-ioctl-lib.c of the
-distribution tar available for download from kernel.org in directory
-/pub/linux/daemons/autofs/v5.
-
-The device node ioctl operations implemented by this interface are:
-
-
-AUTOFS_DEV_IOCTL_VERSION
-------------------------
-
-Get the major and minor version of the autofs4 device ioctl kernel module
-implementation. It requires an initialized struct autofs_dev_ioctl as an
-input parameter and sets the version information in the passed in structure.
-It returns 0 on success or the error -EINVAL if a version mismatch is
-detected.
-
-
-AUTOFS_DEV_IOCTL_PROTOVER_CMD and AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD
-------------------------------------------------------------------
-
-Get the major and minor version of the autofs4 protocol version understood
-by loaded module. This call requires an initialized struct autofs_dev_ioctl
-with the ioctlfd field set to a valid autofs mount point descriptor
-and sets the requested version number in version field of struct args_protover
-or sub_version field of struct args_protosubver. These commands return
-0 on success or one of the negative error codes if validation fails.
-
-
-AUTOFS_DEV_IOCTL_OPENMOUNT and AUTOFS_DEV_IOCTL_CLOSEMOUNT
-----------------------------------------------------------
-
-Obtain and release a file descriptor for an autofs managed mount point
-path. The open call requires an initialized struct autofs_dev_ioctl with
-the path field set and the size field adjusted appropriately as well
-as the devid field of struct args_openmount set to the device number of
-the autofs mount. The device number can be obtained from the mount options
-shown in /proc/mounts. The close call requires an initialized struct
-autofs_dev_ioct with the ioctlfd field set to the descriptor obtained
-from the open call. The release of the file descriptor can also be done
-with close(2) so any open descriptors will also be closed at process exit.
-The close call is included in the implemented operations largely for
-completeness and to provide for a consistent user space implementation.
-
-
-AUTOFS_DEV_IOCTL_READY_CMD and AUTOFS_DEV_IOCTL_FAIL_CMD
---------------------------------------------------------
-
-Return mount and expire result status from user space to the kernel.
-Both of these calls require an initialized struct autofs_dev_ioctl
-with the ioctlfd field set to the descriptor obtained from the open
-call and the token field of struct args_ready or struct args_fail set
-to the wait queue token number, received by user space in the foregoing
-mount or expire request. The status field of struct args_fail is set to
-the errno of the operation. It is set to 0 on success.
-
-
-AUTOFS_DEV_IOCTL_SETPIPEFD_CMD
-------------------------------
-
-Set the pipe file descriptor used for kernel communication to the daemon.
-Normally this is set at mount time using an option but when reconnecting
-to a existing mount we need to use this to tell the autofs mount about
-the new kernel pipe descriptor. In order to protect mounts against
-incorrectly setting the pipe descriptor we also require that the autofs
-mount be catatonic (see next call).
-
-The call requires an initialized struct autofs_dev_ioctl with the
-ioctlfd field set to the descriptor obtained from the open call and
-the pipefd field of struct args_setpipefd set to descriptor of the pipe.
-On success the call also sets the process group id used to identify the
-controlling process (eg. the owning automount(8) daemon) to the process
-group of the caller.
-
-
-AUTOFS_DEV_IOCTL_CATATONIC_CMD
-------------------------------
-
-Make the autofs mount point catatonic. The autofs mount will no longer
-issue mount requests, the kernel communication pipe descriptor is released
-and any remaining waits in the queue released.
-
-The call requires an initialized struct autofs_dev_ioctl with the
-ioctlfd field set to the descriptor obtained from the open call.
-
-
-AUTOFS_DEV_IOCTL_TIMEOUT_CMD
-----------------------------
-
-Set the expire timeout for mounts within an autofs mount point.
-
-The call requires an initialized struct autofs_dev_ioctl with the
-ioctlfd field set to the descriptor obtained from the open call.
-
-
-AUTOFS_DEV_IOCTL_REQUESTER_CMD
-------------------------------
-
-Return the uid and gid of the last process to successfully trigger a the
-mount on the given path dentry.
-
-The call requires an initialized struct autofs_dev_ioctl with the path
-field set to the mount point in question and the size field adjusted
-appropriately. Upon return the uid field of struct args_requester contains
-the uid and gid field the gid.
-
-When reconstructing an autofs mount tree with active mounts we need to
-re-connect to mounts that may have used the original process uid and
-gid (or string variations of them) for mount lookups within the map entry.
-This call provides the ability to obtain this uid and gid so they may be
-used by user space for the mount map lookups.
-
-
-AUTOFS_DEV_IOCTL_EXPIRE_CMD
----------------------------
-
-Issue an expire request to the kernel for an autofs mount. Typically
-this ioctl is called until no further expire candidates are found.
-
-The call requires an initialized struct autofs_dev_ioctl with the
-ioctlfd field set to the descriptor obtained from the open call. In
-addition an immediate expire, independent of the mount timeout, can be
-requested by setting the how field of struct args_expire to 1. If no
-expire candidates can be found the ioctl returns -1 with errno set to
-EAGAIN.
-
-This call causes the kernel module to check the mount corresponding
-to the given ioctlfd for mounts that can be expired, issues an expire
-request back to the daemon and waits for completion.
-
-AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD
-------------------------------
-
-Checks if an autofs mount point is in use.
-
-The call requires an initialized struct autofs_dev_ioctl with the
-ioctlfd field set to the descriptor obtained from the open call and
-it returns the result in the may_umount field of struct args_askumount,
-1 for busy and 0 otherwise.
-
-
-AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD
----------------------------------
-
-Check if the given path is a mountpoint.
-
-The call requires an initialized struct autofs_dev_ioctl. There are two
-possible variations. Both use the path field set to the path of the mount
-point to check and the size field adjusted appropriately. One uses the
-ioctlfd field to identify a specific mount point to check while the other
-variation uses the path and optionally in.type field of struct args_ismountpoint
-set to an autofs mount type. The call returns 1 if this is a mount point
-and sets out.devid field to the device number of the mount and out.magic
-field to the relevant super block magic number (described below) or 0 if
-it isn't a mountpoint. In both cases the the device number (as returned
-by new_encode_dev()) is returned in out.devid field.
-
-If supplied with a file descriptor we're looking for a specific mount,
-not necessarily at the top of the mounted stack. In this case the path
-the descriptor corresponds to is considered a mountpoint if it is itself
-a mountpoint or contains a mount, such as a multi-mount without a root
-mount. In this case we return 1 if the descriptor corresponds to a mount
-point and and also returns the super magic of the covering mount if there
-is one or 0 if it isn't a mountpoint.
-
-If a path is supplied (and the ioctlfd field is set to -1) then the path
-is looked up and is checked to see if it is the root of a mount. If a
-type is also given we are looking for a particular autofs mount and if
-a match isn't found a fail is returned. If the the located path is the
-root of a mount 1 is returned along with the super magic of the mount
-or 0 otherwise.
-
diff --git a/Documentation/filesystems/autofs4.txt b/Documentation/filesystems/autofs4.txt
deleted file mode 100644 (file)
index f10dd59..0000000
+++ /dev/null
@@ -1,529 +0,0 @@
-<head>
-<style> p { max-width:50em} ol, ul {max-width: 40em}</style>
-</head>
-
-autofs - how it works
-=====================
-
-Purpose
--------
-
-The goal of autofs is to provide on-demand mounting and race free
-automatic unmounting of various other filesystems.  This provides two
-key advantages:
-
-1. There is no need to delay boot until all filesystems that
-   might be needed are mounted.  Processes that try to access those
-   slow filesystems might be delayed but other processes can
-   continue freely.  This is particularly important for
-   network filesystems (e.g. NFS) or filesystems stored on
-   media with a media-changing robot.
-
-2. The names and locations of filesystems can be stored in
-   a remote database and can change at any time.  The content
-   in that data base at the time of access will be used to provide
-   a target for the access.  The interpretation of names in the
-   filesystem can even be programmatic rather than database-backed,
-   allowing wildcards for example, and can vary based on the user who
-   first accessed a name.
-
-Context
--------
-
-The "autofs4" filesystem module is only one part of an autofs system.
-There also needs to be a user-space program which looks up names
-and mounts filesystems.  This will often be the "automount" program,
-though other tools including "systemd" can make use of "autofs4".
-This document describes only the kernel module and the interactions
-required with any user-space program.  Subsequent text refers to this
-as the "automount daemon" or simply "the daemon".
-
-"autofs4" is a Linux kernel module with provides the "autofs"
-filesystem type.  Several "autofs" filesystems can be mounted and they
-can each be managed separately, or all managed by the same daemon.
-
-Content
--------
-
-An autofs filesystem can contain 3 sorts of objects: directories,
-symbolic links and mount traps.  Mount traps are directories with
-extra properties as described in the next section.
-
-Objects can only be created by the automount daemon: symlinks are
-created with a regular `symlink` system call, while directories and
-mount traps are created with `mkdir`.  The determination of whether a
-directory should be a mount trap or not is quite _ad hoc_, largely for
-historical reasons, and is determined in part by the
-*direct*/*indirect*/*offset* mount options, and the *maxproto* mount option.
-
-If neither the *direct* or *offset* mount options are given (so the
-mount is considered to be *indirect*), then the root directory is
-always a regular directory, otherwise it is a mount trap when it is
-empty and a regular directory when not empty.  Note that *direct* and
-*offset* are treated identically so a concise summary is that the root
-directory is a mount trap only if the filesystem is mounted *direct*
-and the root is empty.
-
-Directories created in the root directory are mount traps only if the
-filesystem is mounted *indirect* and they are empty.
-
-Directories further down the tree depend on the *maxproto* mount
-option and particularly whether it is less than five or not.
-When *maxproto* is five, no directories further down the
-tree are ever mount traps, they are always regular directories.  When
-the *maxproto* is four (or three), these directories are mount traps
-precisely when they are empty.
-
-So: non-empty (i.e. non-leaf) directories are never mount traps. Empty
-directories are sometimes mount traps, and sometimes not depending on
-where in the tree they are (root, top level, or lower), the *maxproto*,
-and whether the mount was *indirect* or not.
-
-Mount Traps
----------------
-
-A core element of the implementation of autofs is the Mount Traps
-which are provided by the Linux VFS.  Any directory provided by a
-filesystem can be designated as a trap.  This involves two separate
-features that work together to allow autofs to do its job.
-
-**DCACHE_NEED_AUTOMOUNT**
-
-If a dentry has the DCACHE_NEED_AUTOMOUNT flag set (which gets set if
-the inode has S_AUTOMOUNT set, or can be set directly) then it is
-(potentially) a mount trap.  Any access to this directory beyond a
-"`stat`" will (normally) cause the `d_op->d_automount()` dentry operation
-to be called. The task of this method is to find the filesystem that
-should be mounted on the directory and to return it.  The VFS is
-responsible for actually mounting the root of this filesystem on the
-directory.
-
-autofs doesn't find the filesystem itself but sends a message to the
-automount daemon asking it to find and mount the filesystem.  The
-autofs `d_automount` method then waits for the daemon to report that
-everything is ready.  It will then return "`NULL`" indicating that the
-mount has already happened.  The VFS doesn't try to mount anything but
-follows down the mount that is already there.
-
-This functionality is sufficient for some users of mount traps such
-as NFS which creates traps so that mountpoints on the server can be
-reflected on the client.  However it is not sufficient for autofs.  As
-mounting onto a directory is considered to be "beyond a `stat`", the
-automount daemon would not be able to mount a filesystem on the 'trap'
-directory without some way to avoid getting caught in the trap.  For
-that purpose there is another flag.
-
-**DCACHE_MANAGE_TRANSIT**
-
-If a dentry has DCACHE_MANAGE_TRANSIT set then two very different but
-related behaviors are invoked, both using the `d_op->d_manage()`
-dentry operation.
-
-Firstly, before checking to see if any filesystem is mounted on the
-directory, d_manage() will be called with the `rcu_walk` parameter set
-to `false`.  It may return one of three things:
-
--  A return value of zero indicates that there is nothing special
-   about this dentry and normal checks for mounts and automounts
-   should proceed.
-
-   autofs normally returns zero, but first waits for any
-   expiry (automatic unmounting of the mounted filesystem) to
-   complete.  This avoids races.
-
--  A return value of `-EISDIR` tells the VFS to ignore any mounts
-   on the directory and to not consider calling `->d_automount()`.
-   This effectively disables the **DCACHE_NEED_AUTOMOUNT** flag
-   causing the directory not be a mount trap after all.
-
-   autofs returns this if it detects that the process performing the
-   lookup is the automount daemon and that the mount has been
-   requested but has not yet completed.  How it determines this is
-   discussed later.  This allows the automount daemon not to get
-   caught in the mount trap.
-
-   There is a subtlety here.  It is possible that a second autofs
-   filesystem can be mounted below the first and for both of them to
-   be managed by the same daemon.  For the daemon to be able to mount
-   something on the second it must be able to "walk" down past the
-   first.  This means that d_manage cannot *always* return -EISDIR for
-   the automount daemon.  It must only return it when a mount has
-   been requested, but has not yet completed.
-
-   `d_manage` also returns `-EISDIR` if the dentry shouldn't be a
-   mount trap, either because it is a symbolic link or because it is
-   not empty.
-
--  Any other negative value is treated as an error and returned
-   to the caller.
-
-   autofs can return
-
-   - -ENOENT if the automount daemon failed to mount anything,
-   - -ENOMEM if it ran out of memory,
-   - -EINTR if a signal arrived while waiting for expiry to
-     complete
-   - or any other error sent down by the automount daemon.
-
-
-The second use case only occurs during an "RCU-walk" and so `rcu_walk`
-will be set.
-
-An RCU-walk is a fast and lightweight process for walking down a
-filename path (i.e. it is like running on tip-toes).  RCU-walk cannot
-cope with all situations so when it finds a difficulty it falls back
-to "REF-walk", which is slower but more robust.
-
-RCU-walk will never call `->d_automount`; the filesystems must already
-be mounted or RCU-walk cannot handle the path.
-To determine if a mount-trap is safe for RCU-walk mode it calls
-`->d_manage()` with `rcu_walk` set to `true`.
-
-In this case `d_manage()` must avoid blocking and should avoid taking
-spinlocks if at all possible.  Its sole purpose is to determine if it
-would be safe to follow down into any mounted directory and the only
-reason that it might not be is if an expiry of the mount is
-underway.
-
-In the `rcu_walk` case, `d_manage()` cannot return -EISDIR to tell the
-VFS that this is a directory that doesn't require d_automount.  If
-`rcu_walk` sees a dentry with DCACHE_NEED_AUTOMOUNT set but nothing
-mounted, it *will* fall back to REF-walk.  `d_manage()` cannot make the
-VFS remain in RCU-walk mode, but can only tell it to get out of
-RCU-walk mode by returning `-ECHILD`.
-
-So `d_manage()`, when called with `rcu_walk` set, should either return
--ECHILD if there is any reason to believe it is unsafe to end the
-mounted filesystem, and otherwise should return 0.
-
-autofs will return `-ECHILD` if an expiry of the filesystem has been
-initiated or is being considered, otherwise it returns 0.
-
-
-Mountpoint expiry
------------------
-
-The VFS has a mechanism for automatically expiring unused mounts,
-much as it can expire any unused dentry information from the dcache.
-This is guided by the MNT_SHRINKABLE flag.  This only applies to
-mounts that were created by `d_automount()` returning a filesystem to be
-mounted.  As autofs doesn't return such a filesystem but leaves the
-mounting to the automount daemon, it must involve the automount daemon
-in unmounting as well.  This also means that autofs has more control
-of expiry.
-
-The VFS also supports "expiry" of mounts using the MNT_EXPIRE flag to
-the `umount` system call.  Unmounting with MNT_EXPIRE will fail unless
-a previous attempt had been made, and the filesystem has been inactive
-and untouched since that previous attempt.  autofs4 does not depend on
-this but has its own internal tracking of whether filesystems were
-recently used.  This allows individual names in the autofs directory
-to expire separately.
-
-With version 4 of the protocol, the automount daemon can try to
-unmount any filesystems mounted on the autofs filesystem or remove any
-symbolic links or empty directories any time it likes.  If the unmount
-or removal is successful the filesystem will be returned to the state
-it was before the mount or creation, so that any access of the name
-will trigger normal auto-mount processing.  In particlar, `rmdir` and
-`unlink` do not leave negative entries in the dcache as a normal
-filesystem would, so an attempt to access a recently-removed object is
-passed to autofs for handling.
-
-With version 5, this is not safe except for unmounting from top-level
-directories.  As lower-level directories are never mount traps, other
-processes will see an empty directory as soon as the filesystem is
-unmounted.  So it is generally safest to use the autofs expiry
-protocol described below.
-
-Normally the daemon only wants to remove entries which haven't been
-used for a while.  For this purpose autofs maintains a "`last_used`"
-time stamp on each directory or symlink.  For symlinks it genuinely
-does record the last time the symlink was "used" or followed to find
-out where it points to.  For directories the field is a slight
-misnomer.  It actually records the last time that autofs checked if
-the directory or one of its descendents was busy and found that it
-was.  This is just as useful and doesn't require updating the field so
-often.
-
-The daemon is able to ask autofs if anything is due to be expired,
-using an `ioctl` as discussed later.  For a *direct* mount, autofs
-considers if the entire mount-tree can be unmounted or not.  For an
-*indirect* mount, autofs considers each of the names in the top level
-directory to determine if any of those can be unmounted and cleaned
-up.
-
-There is an option with indirect mounts to consider each of the leaves
-that has been mounted on instead of considering the top-level names.
-This is intended for compatability with version 4 of autofs and should
-be considered as deprecated.
-
-When autofs considers a directory it checks the `last_used` time and
-compares it with the "timeout" value set when the filesystem was
-mounted, though this check is ignored in some cases. It also checks if
-the directory or anything below it is in use.  For symbolic links,
-only the `last_used` time is ever considered.
-
-If both appear to support expiring the directory or symlink, an action
-is taken.
-
-There are two ways to ask autofs to consider expiry.  The first is to
-use the **AUTOFS_IOC_EXPIRE** ioctl.  This only works for indirect
-mounts.  If it finds something in the root directory to expire it will
-return the name of that thing.  Once a name has been returned the
-automount daemon needs to unmount any filesystems mounted below the
-name normally.  As described above, this is unsafe for non-toplevel
-mounts in a version-5 autofs.  For this reason the current `automountd`
-does not use this ioctl.
-
-The second mechanism uses either the **AUTOFS_DEV_IOCTL_EXPIRE_CMD** or
-the **AUTOFS_IOC_EXPIRE_MULTI** ioctl.  This will work for both direct and
-indirect mounts.  If it selects an object to expire, it will notify
-the daemon using the notification mechanism described below.  This
-will block until the daemon acknowledges the expiry notification.
-This implies that the "`EXPIRE`" ioctl must be sent from a different
-thread than the one which handles notification.
-
-While the ioctl is blocking, the entry is marked as "expiring" and
-`d_manage` will block until the daemon affirms that the unmount has
-completed (together with removing any directories that might have been
-necessary), or has been aborted.
-
-Communicating with autofs: detecting the daemon
------------------------------------------------
-
-There are several forms of communication between the automount daemon
-and the filesystem.  As we have already seen, the daemon can create and
-remove directories and symlinks using normal filesystem operations.
-autofs knows whether a process requesting some operation is the daemon
-or not based on its process-group id number (see getpgid(1)).
-
-When an autofs filesystem is mounted the pgid of the mounting
-processes is recorded unless the "pgrp=" option is given, in which
-case that number is recorded instead.  Any request arriving from a
-process in that process group is considered to come from the daemon.
-If the daemon ever has to be stopped and restarted a new pgid can be
-provided through an ioctl as will be described below.
-
-Communicating with autofs: the event pipe
------------------------------------------
-
-When an autofs filesystem is mounted, the 'write' end of a pipe must
-be passed using the 'fd=' mount option.  autofs will write
-notification messages to this pipe for the daemon to respond to.
-For version 5, the format of the message is:
-
-        struct autofs_v5_packet {
-                int proto_version;                /* Protocol version */
-                int type;                        /* Type of packet */
-                autofs_wqt_t wait_queue_token;
-                __u32 dev;
-                __u64 ino;
-                __u32 uid;
-                __u32 gid;
-                __u32 pid;
-                __u32 tgid;
-                __u32 len;
-                char name[NAME_MAX+1];
-        };
-
-where the type is one of
-
-        autofs_ptype_missing_indirect
-        autofs_ptype_expire_indirect
-        autofs_ptype_missing_direct
-        autofs_ptype_expire_direct
-
-so messages can indicate that a name is missing (something tried to
-access it but it isn't there) or that it has been selected for expiry.
-
-The pipe will be set to "packet mode" (equivalent to passing
-`O_DIRECT`) to _pipe2(2)_ so that a read from the pipe will return at
-most one packet, and any unread portion of a packet will be discarded.
-
-The `wait_queue_token` is a unique number which can identify a
-particular request to be acknowledged.  When a message is sent over
-the pipe the affected dentry is marked as either "active" or
-"expiring" and other accesses to it block until the message is
-acknowledged using one of the ioctls below and the relevant
-`wait_queue_token`.
-
-Communicating with autofs: root directory ioctls
-------------------------------------------------
-
-The root directory of an autofs filesystem will respond to a number of
-ioctls.  The process issuing the ioctl must have the CAP_SYS_ADMIN
-capability, or must be the automount daemon.
-
-The available ioctl commands are:
-
-- **AUTOFS_IOC_READY**: a notification has been handled.  The argument
-    to the ioctl command is the "wait_queue_token" number
-    corresponding to the notification being acknowledged.
-- **AUTOFS_IOC_FAIL**: similar to above, but indicates failure with
-    the error code `ENOENT`.
-- **AUTOFS_IOC_CATATONIC**: Causes the autofs to enter "catatonic"
-    mode meaning that it stops sending notifications to the daemon.
-    This mode is also entered if a write to the pipe fails.
-- **AUTOFS_IOC_PROTOVER**:  This returns the protocol version in use.
-- **AUTOFS_IOC_PROTOSUBVER**: Returns the protocol sub-version which
-    is really a version number for the implementation.  It is
-    currently 2.
-- **AUTOFS_IOC_SETTIMEOUT**:  This passes a pointer to an unsigned
-    long.  The value is used to set the timeout for expiry, and
-    the current timeout value is stored back through the pointer.
-- **AUTOFS_IOC_ASKUMOUNT**:  Returns, in the pointed-to `int`, 1 if
-    the filesystem could be unmounted.  This is only a hint as
-    the situation could change at any instant.  This call can be
-    use to avoid a more expensive full unmount attempt.
-- **AUTOFS_IOC_EXPIRE**: as described above, this asks if there is
-    anything suitable to expire.  A pointer to a packet:
-
-        struct autofs_packet_expire_multi {
-                int proto_version;              /* Protocol version */
-                int type;                       /* Type of packet */
-                autofs_wqt_t wait_queue_token;
-                int len;
-                char name[NAME_MAX+1];
-        };
-
-     is required.  This is filled in with the name of something
-     that can be unmounted or removed.  If nothing can be expired,
-     `errno` is set to `EAGAIN`.  Even though a `wait_queue_token`
-     is present in the structure, no "wait queue" is established
-     and no acknowledgment is needed.
-- **AUTOFS_IOC_EXPIRE_MULTI**:  This is similar to
-     **AUTOFS_IOC_EXPIRE** except that it causes notification to be
-     sent to the daemon, and it blocks until the daemon acknowledges.
-     The argument is an integer which can contain two different flags.
-
-     **AUTOFS_EXP_IMMEDIATE** causes `last_used` time to be ignored
-     and objects are expired if the are not in use.
-
-     **AUTOFS_EXP_LEAVES** will select a leaf rather than a top-level
-     name to expire.  This is only safe when *maxproto* is 4.
-
-Communicating with autofs: char-device ioctls
----------------------------------------------
-
-It is not always possible to open the root of an autofs filesystem,
-particularly a *direct* mounted filesystem.  If the automount daemon
-is restarted there is no way for it to regain control of existing
-mounts using any of the above communication channels.  To address this
-need there is a "miscellaneous" character device (major 10, minor 235)
-which can be used to communicate directly with the autofs filesystem.
-It requires CAP_SYS_ADMIN for access.
-
-The `ioctl`s that can be used on this device are described in a separate
-document `autofs4-mount-control.txt`, and are summarized briefly here.
-Each ioctl is passed a pointer to an `autofs_dev_ioctl` structure:
-
-        struct autofs_dev_ioctl {
-                __u32 ver_major;
-                __u32 ver_minor;
-                __u32 size;             /* total size of data passed in
-                                         * including this struct */
-                __s32 ioctlfd;          /* automount command fd */
-
-               /* Command parameters */
-               union {
-                       struct args_protover            protover;
-                       struct args_protosubver         protosubver;
-                       struct args_openmount           openmount;
-                       struct args_ready               ready;
-                       struct args_fail                fail;
-                       struct args_setpipefd           setpipefd;
-                       struct args_timeout             timeout;
-                       struct args_requester           requester;
-                       struct args_expire              expire;
-                       struct args_askumount           askumount;
-                       struct args_ismountpoint        ismountpoint;
-               };
-
-                char path[0];
-        };
-
-For the **OPEN_MOUNT** and **IS_MOUNTPOINT** commands, the target
-filesystem is identified by the `path`.  All other commands identify
-the filesystem by the `ioctlfd` which is a file descriptor open on the
-root, and which can be returned by **OPEN_MOUNT**.
-
-The `ver_major` and `ver_minor` are in/out parameters which check that
-the requested version is supported, and report the maximum version
-that the kernel module can support.
-
-Commands are:
-
-- **AUTOFS_DEV_IOCTL_VERSION_CMD**: does nothing, except validate and
-    set version numbers.
-- **AUTOFS_DEV_IOCTL_OPENMOUNT_CMD**: return an open file descriptor
-    on the root of an autofs filesystem.  The filesystem is identified
-    by name and device number, which is stored in `openmount.devid`.
-    Device numbers for existing filesystems can be found in
-    `/proc/self/mountinfo`.
-- **AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD**: same as `close(ioctlfd)`.
-- **AUTOFS_DEV_IOCTL_SETPIPEFD_CMD**: if the filesystem is in
-    catatonic mode, this can provide the write end of a new pipe
-    in `setpipefd.pipefd` to re-establish communication with a daemon.
-    The process group of the calling process is used to identify the
-    daemon.
-- **AUTOFS_DEV_IOCTL_REQUESTER_CMD**: `path` should be a
-    name within the filesystem that has been auto-mounted on.
-    On successful return, `requester.uid` and `requester.gid` will be
-    the UID and GID of the process which triggered that mount.
-- **AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD**: Check if path is a
-    mountpoint of a particular type - see separate documentation for
-    details.
-- **AUTOFS_DEV_IOCTL_PROTOVER_CMD**:
-- **AUTOFS_DEV_IOCTL_PROTOSUBVER_CMD**:
-- **AUTOFS_DEV_IOCTL_READY_CMD**:
-- **AUTOFS_DEV_IOCTL_FAIL_CMD**:
-- **AUTOFS_DEV_IOCTL_CATATONIC_CMD**:
-- **AUTOFS_DEV_IOCTL_TIMEOUT_CMD**:
-- **AUTOFS_DEV_IOCTL_EXPIRE_CMD**:
-- **AUTOFS_DEV_IOCTL_ASKUMOUNT_CMD**:  These all have the same
-    function as the similarly named **AUTOFS_IOC** ioctls, except
-    that **FAIL** can be given an explicit error number in `fail.status`
-    instead of assuming `ENOENT`, and this **EXPIRE** command
-    corresponds to **AUTOFS_IOC_EXPIRE_MULTI**.
-
-Catatonic mode
---------------
-
-As mentioned, an autofs mount can enter "catatonic" mode.  This
-happens if a write to the notification pipe fails, or if it is
-explicitly requested by an `ioctl`.
-
-When entering catatonic mode, the pipe is closed and any pending
-notifications are acknowledged with the error `ENOENT`.
-
-Once in catatonic mode attempts to access non-existing names will
-result in `ENOENT` while attempts to access existing directories will
-be treated in the same way as if they came from the daemon, so mount
-traps will not fire.
-
-When the filesystem is mounted a _uid_ and _gid_ can be given which
-set the ownership of directories and symbolic links.  When the
-filesystem is in catatonic mode, any process with a matching UID can
-create directories or symlinks in the root directory, but not in other
-directories.
-
-Catatonic mode can only be left via the
-**AUTOFS_DEV_IOCTL_OPENMOUNT_CMD** ioctl on the `/dev/autofs`.
-
-autofs, name spaces, and shared mounts
---------------------------------------
-
-With bind mounts and name spaces it is possible for an autofs
-filesystem to appear at multiple places in one or more filesystem
-name spaces.  For this to work sensibly, the autofs filesystem should
-always be mounted "shared". e.g.
-
-> `mount --make-shared /autofs/mount/point`
-
-The automount daemon is only able to manage a single mount location for
-an autofs filesystem and if mounts on that are not 'shared', other
-locations will not behave as expected.  In particular access to those
-other locations will likely result in the `ELOOP` error
-
-> Too many levels of symbolic links
index 7eb762eb31361739bac381a7453ecb449facf161..b0afd3d55eaf15c8d1c07b838c2f88d47d5a56c4 100644 (file)
@@ -9,7 +9,7 @@ also be requested by userspace.
 IN-KERNEL AUTOMOUNTING
 ======================
 
-See section "Mount Traps" of  Documentation/filesystems/autofs4.txt
+See section "Mount Traps" of  Documentation/filesystems/autofs.txt
 
 Then from userspace, you can just do something like:
 
index 1933ef734e63b03802b42ce236b2b664b239dd93..e2edd45c4bc0882260b2506bdd9b1929d746e6b7 100644 (file)
@@ -460,7 +460,7 @@ this retry process in the next article.
 Automount points are locations in the filesystem where an attempt to
 lookup a name can trigger changes to how that lookup should be
 handled, in particular by mounting a filesystem there.  These are
-covered in greater detail in autofs4.txt in the Linux documentation
+covered in greater detail in autofs.txt in the Linux documentation
 tree, but a few notes specifically related to path lookup are in order
 here.
 
index d325d2dc76002e3bb756bf1e9791914dc1bbe0c0..c9ac159fb02356003fabc3c9042e2843569acce5 100644 (file)
@@ -7723,11 +7723,11 @@ W:      https://linuxtv.org
 S:     Maintained
 F:     drivers/media/radio/radio-keene*
 
-KERNEL AUTOMOUNTER v4 (AUTOFS4)
+KERNEL AUTOMOUNTER
 M:     Ian Kent <raven@themaw.net>
 L:     autofs@vger.kernel.org
 S:     Maintained
-F:     fs/autofs4/
+F:     fs/autofs/
 
 KERNEL BUILD + files below scripts/ (unless maintained elsewhere)
 M:     Masahiro Yamada <yamada.masahiro@socionext.com>
index 89d47eac18b28fd663ffeee6ce18fc9c165a22c9..e81bcd271be72e7b1e2bbece5b2b7442ebd7b462 100644 (file)
@@ -48,6 +48,7 @@ config ARC
        select HAVE_GENERIC_DMA_COHERENT
        select HAVE_KERNEL_GZIP
        select HAVE_KERNEL_LZMA
+       select ARCH_HAS_PTE_SPECIAL
 
 config MIGHT_HAVE_PCI
        bool
index 08fe33830d4b17337757d548399c897aa240de36..8ec5599a0957e3f2314a63450f46588125d94a82 100644 (file)
@@ -320,8 +320,6 @@ PTE_BIT_FUNC(mkexec,        |= (_PAGE_EXECUTE));
 PTE_BIT_FUNC(mkspecial,        |= (_PAGE_SPECIAL));
 PTE_BIT_FUNC(mkhuge,   |= (_PAGE_HW_SZ));
 
-#define __HAVE_ARCH_PTE_SPECIAL
-
 static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
 {
        return __pte((pte_val(pte) & _PAGE_CHG_MASK) | pgprot_val(newprot));
index 8f460bdd4be1c6014bea8b3f69c0406c2e18a976..534563ac7f5f2115abff0e76703ae4f8baeeb57a 100644 (file)
@@ -8,6 +8,7 @@ config ARM
        select ARCH_HAS_DEVMEM_IS_ALLOWED
        select ARCH_HAS_ELF_RANDOMIZE
        select ARCH_HAS_FORTIFY_SOURCE
+       select ARCH_HAS_PTE_SPECIAL if ARM_LPAE
        select ARCH_HAS_SET_MEMORY
        select ARCH_HAS_PHYS_TO_DMA
        select ARCH_HAS_STRICT_KERNEL_RWX if MMU && !XIP_KERNEL
index 2a4836087358bcb51bc844b1eabe5dbf58c2dc37..6d50a11d779345349cd2155fa433e3b9d6a75d08 100644 (file)
@@ -219,7 +219,6 @@ static inline pte_t pte_mkspecial(pte_t pte)
        pte_val(pte) |= L_PTE_SPECIAL;
        return pte;
 }
-#define        __HAVE_ARCH_PTE_SPECIAL
 
 #define pmd_write(pmd)         (pmd_isclear((pmd), L_PMD_SECT_RDONLY))
 #define pmd_dirty(pmd)         (pmd_isset((pmd), L_PMD_SECT_DIRTY))
index b25ed7834f6c354a5363e5b80e7da05d705d2861..4759566a78cbe303a5397c4a2ead8f1467585a41 100644 (file)
@@ -17,6 +17,7 @@ config ARM64
        select ARCH_HAS_GIGANTIC_PAGE if (MEMORY_ISOLATION && COMPACTION) || CMA
        select ARCH_HAS_KCOV
        select ARCH_HAS_MEMBARRIER_SYNC_CORE
+       select ARCH_HAS_PTE_SPECIAL
        select ARCH_HAS_SET_MEMORY
        select ARCH_HAS_SG_CHAIN
        select ARCH_HAS_STRICT_KERNEL_RWX
index 7c4c8f318ba999afdac0b41300c9613fe161e91d..9f82d6b53851e4b6bedbb28f6d0e7480acd622a6 100644 (file)
@@ -306,8 +306,6 @@ static inline int pte_same(pte_t pte_a, pte_t pte_b)
 #define HPAGE_MASK             (~(HPAGE_SIZE - 1))
 #define HUGETLB_PAGE_ORDER     (HPAGE_SHIFT - PAGE_SHIFT)
 
-#define __HAVE_ARCH_PTE_SPECIAL
-
 static inline pte_t pgd_pte(pgd_t pgd)
 {
        return __pte(pgd_val(pgd));
index 076fe309485631cc040245d06e00dfd7978cbdbe..8f959df2de7ac4b625c6e28227821ce7952bfb52 100644 (file)
@@ -135,6 +135,7 @@ config PPC
        select ARCH_HAS_GCOV_PROFILE_ALL
        select ARCH_HAS_PHYS_TO_DMA
        select ARCH_HAS_PMEM_API                if PPC64
+       select ARCH_HAS_PTE_SPECIAL
        select ARCH_HAS_MEMBARRIER_CALLBACKS
        select ARCH_HAS_SCALED_CPUTIME          if VIRT_CPU_ACCOUNTING_NATIVE
        select ARCH_HAS_SG_CHAIN
index 42fe7c2ff2df9f8a86142b3b614f07113842becb..63cee159022b51400fbc52dd21ebd31f55f3db67 100644 (file)
@@ -335,9 +335,6 @@ extern unsigned long pci_io_base;
 /* Advertise special mapping type for AGP */
 #define HAVE_PAGE_AGP
 
-/* Advertise support for _PAGE_SPECIAL */
-#define __HAVE_ARCH_PTE_SPECIAL
-
 #ifndef __ASSEMBLY__
 
 /*
index 050b0d77532457c914af540f55255b5afe81471d..bef56141a549edac30af3ea20f7d1e6f759ba1fb 100644 (file)
@@ -208,9 +208,6 @@ static inline bool pte_user(pte_t pte)
 #define PAGE_AGP               (PAGE_KERNEL_NC)
 #define HAVE_PAGE_AGP
 
-/* Advertise support for _PAGE_SPECIAL */
-#define __HAVE_ARCH_PTE_SPECIAL
-
 #ifndef _PAGE_READ
 /* if not defined, we should not find _PAGE_WRITE too */
 #define _PAGE_READ 0
index 274bc064c41f84698b7023d616a243eebc57adcc..17f19e67993b10e462abb9ad8b3e88b8da6ee5df 100644 (file)
@@ -42,6 +42,7 @@ config RISCV
        select THREAD_INFO_IN_TASK
        select RISCV_TIMER
        select GENERIC_IRQ_MULTI_HANDLER
+       select ARCH_HAS_PTE_SPECIAL
 
 config MMU
        def_bool y
index 997ddbb1d37092abf4b54636fea035b5c678a207..2fa2942be221e480088ab1a5681c9240ef3a1e23 100644 (file)
@@ -42,7 +42,4 @@
                                          _PAGE_WRITE | _PAGE_EXEC |    \
                                          _PAGE_USER | _PAGE_GLOBAL))
 
-/* Advertise support for _PAGE_SPECIAL */
-#define __HAVE_ARCH_PTE_SPECIAL
-
 #endif /* _ASM_RISCV_PGTABLE_BITS_H */
index b7deee7e738f59ac52a84d236b0ff4657783d3c8..baed39772c845d74d91c292aba3b3ea4063aa130 100644 (file)
@@ -65,6 +65,7 @@ config S390
        select ARCH_HAS_GCOV_PROFILE_ALL
        select ARCH_HAS_GIGANTIC_PAGE if (MEMORY_ISOLATION && COMPACTION) || CMA
        select ARCH_HAS_KCOV
+       select ARCH_HAS_PTE_SPECIAL
        select ARCH_HAS_SET_MEMORY
        select ARCH_HAS_SG_CHAIN
        select ARCH_HAS_STRICT_KERNEL_RWX
index 2d24d33bf188a0957927a6addd8a7eb38a5319f6..9809694e1389ef836f4d5ea1eb4d9b08be5e1260 100644 (file)
@@ -171,7 +171,6 @@ static inline int is_module_addr(void *addr)
 #define _PAGE_WRITE    0x020           /* SW pte write bit */
 #define _PAGE_SPECIAL  0x040           /* SW associated with special page */
 #define _PAGE_UNUSED   0x080           /* SW bit for pgste usage state */
-#define __HAVE_ARCH_PTE_SPECIAL
 
 #ifdef CONFIG_MEM_SOFT_DIRTY
 #define _PAGE_SOFT_DIRTY 0x002         /* SW pte soft dirty bit */
index 562f729559567e084fade524cbfa386c51cb1283..84bd6329a88dd3ace39e612197dccec0a48dc4fc 100644 (file)
@@ -190,14 +190,15 @@ unsigned long *page_table_alloc(struct mm_struct *mm)
                if (!list_empty(&mm->context.pgtable_list)) {
                        page = list_first_entry(&mm->context.pgtable_list,
                                                struct page, lru);
-                       mask = atomic_read(&page->_mapcount);
+                       mask = atomic_read(&page->_refcount) >> 24;
                        mask = (mask | (mask >> 4)) & 3;
                        if (mask != 3) {
                                table = (unsigned long *) page_to_phys(page);
                                bit = mask & 1;         /* =1 -> second 2K */
                                if (bit)
                                        table += PTRS_PER_PTE;
-                               atomic_xor_bits(&page->_mapcount, 1U << bit);
+                               atomic_xor_bits(&page->_refcount,
+                                                       1U << (bit + 24));
                                list_del(&page->lru);
                        }
                }
@@ -218,12 +219,12 @@ unsigned long *page_table_alloc(struct mm_struct *mm)
        table = (unsigned long *) page_to_phys(page);
        if (mm_alloc_pgste(mm)) {
                /* Return 4K page table with PGSTEs */
-               atomic_set(&page->_mapcount, 3);
+               atomic_xor_bits(&page->_refcount, 3 << 24);
                memset64((u64 *)table, _PAGE_INVALID, PTRS_PER_PTE);
                memset64((u64 *)table + PTRS_PER_PTE, 0, PTRS_PER_PTE);
        } else {
                /* Return the first 2K fragment of the page */
-               atomic_set(&page->_mapcount, 1);
+               atomic_xor_bits(&page->_refcount, 1 << 24);
                memset64((u64 *)table, _PAGE_INVALID, 2 * PTRS_PER_PTE);
                spin_lock_bh(&mm->context.lock);
                list_add(&page->lru, &mm->context.pgtable_list);
@@ -242,7 +243,8 @@ void page_table_free(struct mm_struct *mm, unsigned long *table)
                /* Free 2K page table fragment of a 4K page */
                bit = (__pa(table) & ~PAGE_MASK)/(PTRS_PER_PTE*sizeof(pte_t));
                spin_lock_bh(&mm->context.lock);
-               mask = atomic_xor_bits(&page->_mapcount, 1U << bit);
+               mask = atomic_xor_bits(&page->_refcount, 1U << (bit + 24));
+               mask >>= 24;
                if (mask & 3)
                        list_add(&page->lru, &mm->context.pgtable_list);
                else
@@ -253,7 +255,6 @@ void page_table_free(struct mm_struct *mm, unsigned long *table)
        }
 
        pgtable_page_dtor(page);
-       atomic_set(&page->_mapcount, -1);
        __free_page(page);
 }
 
@@ -274,7 +275,8 @@ void page_table_free_rcu(struct mmu_gather *tlb, unsigned long *table,
        }
        bit = (__pa(table) & ~PAGE_MASK) / (PTRS_PER_PTE*sizeof(pte_t));
        spin_lock_bh(&mm->context.lock);
-       mask = atomic_xor_bits(&page->_mapcount, 0x11U << bit);
+       mask = atomic_xor_bits(&page->_refcount, 0x11U << (bit + 24));
+       mask >>= 24;
        if (mask & 3)
                list_add_tail(&page->lru, &mm->context.pgtable_list);
        else
@@ -296,12 +298,13 @@ static void __tlb_remove_table(void *_table)
                break;
        case 1:         /* lower 2K of a 4K page table */
        case 2:         /* higher 2K of a 4K page table */
-               if (atomic_xor_bits(&page->_mapcount, mask << 4) != 0)
+               mask = atomic_xor_bits(&page->_refcount, mask << (4 + 24));
+               mask >>= 24;
+               if (mask != 0)
                        break;
                /* fallthrough */
        case 3:         /* 4K page table with pgstes */
                pgtable_page_dtor(page);
-               atomic_set(&page->_mapcount, -1);
                __free_page(page);
                break;
        }
index ae619d54018c570b13f9a9ad9f9018c9a5f95e34..4d61a085982b3e2a669141372fd38c40c6ebb851 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 config SUPERH
        def_bool y
+       select ARCH_HAS_PTE_SPECIAL
        select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
        select ARCH_MIGHT_HAVE_PC_PARPORT
        select ARCH_NO_COHERENT_DMA_MMAP if !MMU
index 89c513a982fcbbd83ac87a77e0c95c32782b73e6..f6abfe2bca93b8254278f635420b1fbc410b4a99 100644 (file)
@@ -156,8 +156,6 @@ extern void page_table_range_init(unsigned long start, unsigned long end,
 #define HAVE_ARCH_UNMAPPED_AREA
 #define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN
 
-#define __HAVE_ARCH_PTE_SPECIAL
-
 #include <asm-generic/pgtable.h>
 
 #endif /* __ASM_SH_PGTABLE_H */
index b42ba888217d573139e34d93644db8728382a09a..9a2b8877f1749a4ec067032c7c2002755f45e5d9 100644 (file)
@@ -88,6 +88,7 @@ config SPARC64
        select ARCH_USE_QUEUED_SPINLOCKS
        select GENERIC_TIME_VSYSCALL
        select ARCH_CLOCKSOURCE_DATA
+       select ARCH_HAS_PTE_SPECIAL
 
 config ARCH_DEFCONFIG
        string
index 44d6ac47e035f8fe65b91771e8a758dffae4ae92..1393a8ac596b0a050433b1afcc7d8a97302f47f2 100644 (file)
@@ -117,9 +117,6 @@ bool kern_addr_valid(unsigned long addr);
 #define _PAGE_PMD_HUGE    _AC(0x0100000000000000,UL) /* Huge page            */
 #define _PAGE_PUD_HUGE    _PAGE_PMD_HUGE
 
-/* Advertise support for _PAGE_SPECIAL */
-#define __HAVE_ARCH_PTE_SPECIAL
-
 /* SUN4U pte bits... */
 #define _PAGE_SZ4MB_4U   _AC(0x6000000000000000,UL) /* 4MB Page             */
 #define _PAGE_SZ512K_4U          _AC(0x4000000000000000,UL) /* 512K Page            */
index cb6e3a21929473ca003b8227a6f97fc4b8335f79..f182a4e8e5bd24cb58bf0ce8a659adb5aa0c8d81 100644 (file)
@@ -60,6 +60,7 @@ config X86
        select ARCH_HAS_KCOV                    if X86_64
        select ARCH_HAS_MEMBARRIER_SYNC_CORE
        select ARCH_HAS_PMEM_API                if X86_64
+       select ARCH_HAS_PTE_SPECIAL
        select ARCH_HAS_REFCOUNT
        select ARCH_HAS_UACCESS_FLUSHCACHE      if X86_64
        select ARCH_HAS_UACCESS_MCSAFE          if X86_64
index 1e5a406739536d2376b81845899710017896367b..99fff853c9444466b6bd63163da17d9676773647 100644 (file)
@@ -65,7 +65,6 @@
 #define _PAGE_PKEY_BIT2        (_AT(pteval_t, 0))
 #define _PAGE_PKEY_BIT3        (_AT(pteval_t, 0))
 #endif
-#define __HAVE_ARCH_PTE_SPECIAL
 
 #define _PAGE_PKEY_MASK (_PAGE_PKEY_BIT0 | \
                         _PAGE_PKEY_BIT1 | \
index ffc8c13c50e4491941a1f795e2339964305e51da..938dbcd46b97b2827400e2f71813f50bb26622d4 100644 (file)
@@ -114,13 +114,12 @@ static inline void pgd_list_del(pgd_t *pgd)
 
 static void pgd_set_mm(pgd_t *pgd, struct mm_struct *mm)
 {
-       BUILD_BUG_ON(sizeof(virt_to_page(pgd)->index) < sizeof(mm));
-       virt_to_page(pgd)->index = (pgoff_t)mm;
+       virt_to_page(pgd)->pt_mm = mm;
 }
 
 struct mm_struct *pgd_page_get_mm(struct page *page)
 {
-       return (struct mm_struct *)page->index;
+       return page->pt_mm;
 }
 
 static void pgd_ctor(struct mm_struct *mm, pgd_t *pgd)
index ac3a31d433b2e9f698d051bfcd06f76d0efad971..635235759a0ab06b593c2d100f8b9f2ec75cc392 100644 (file)
@@ -13,7 +13,7 @@ config ZRAM
          It has several use cases, for example: /tmp storage, use as swap
          disks and maybe many more.
 
-         See zram.txt for more information.
+         See Documentation/blockdev/zram.txt for more information.
 
 config ZRAM_WRITEBACK
        bool "Write back incompressible page to backing device"
@@ -25,4 +25,14 @@ config ZRAM_WRITEBACK
         For this feature, admin should set up backing device via
         /sys/block/zramX/backing_dev.
 
-        See zram.txt for more infomration.
+        See Documentation/blockdev/zram.txt for more information.
+
+config ZRAM_MEMORY_TRACKING
+       bool "Track zRam block status"
+       depends on ZRAM && DEBUG_FS
+       help
+         With this feature, admin can track the state of allocated blocks
+         of zRAM. Admin could see the information via
+         /sys/kernel/debug/zram/zramX/block_state.
+
+         See Documentation/blockdev/zram.txt for more information.
index 0f3fadd712307efc7b5cf343d18fb42bd76cbd43..da51293e7c03c172ba824e303d5bbc4ae3d7d745 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/err.h>
 #include <linux/idr.h>
 #include <linux/sysfs.h>
+#include <linux/debugfs.h>
 #include <linux/cpuhotplug.h>
 
 #include "zram_drv.h"
@@ -52,11 +53,28 @@ static size_t huge_class_size;
 
 static void zram_free_page(struct zram *zram, size_t index);
 
+static void zram_slot_lock(struct zram *zram, u32 index)
+{
+       bit_spin_lock(ZRAM_LOCK, &zram->table[index].value);
+}
+
+static void zram_slot_unlock(struct zram *zram, u32 index)
+{
+       bit_spin_unlock(ZRAM_LOCK, &zram->table[index].value);
+}
+
 static inline bool init_done(struct zram *zram)
 {
        return zram->disksize;
 }
 
+static inline bool zram_allocated(struct zram *zram, u32 index)
+{
+
+       return (zram->table[index].value >> (ZRAM_FLAG_SHIFT + 1)) ||
+                                       zram->table[index].handle;
+}
+
 static inline struct zram *dev_to_zram(struct device *dev)
 {
        return (struct zram *)dev_to_disk(dev)->private_data;
@@ -73,7 +91,7 @@ static void zram_set_handle(struct zram *zram, u32 index, unsigned long handle)
 }
 
 /* flag operations require table entry bit_spin_lock() being held */
-static int zram_test_flag(struct zram *zram, u32 index,
+static bool zram_test_flag(struct zram *zram, u32 index,
                        enum zram_pageflags flag)
 {
        return zram->table[index].value & BIT(flag);
@@ -600,6 +618,114 @@ static int read_from_bdev(struct zram *zram, struct bio_vec *bvec,
 static void zram_wb_clear(struct zram *zram, u32 index) {}
 #endif
 
+#ifdef CONFIG_ZRAM_MEMORY_TRACKING
+
+static struct dentry *zram_debugfs_root;
+
+static void zram_debugfs_create(void)
+{
+       zram_debugfs_root = debugfs_create_dir("zram", NULL);
+}
+
+static void zram_debugfs_destroy(void)
+{
+       debugfs_remove_recursive(zram_debugfs_root);
+}
+
+static void zram_accessed(struct zram *zram, u32 index)
+{
+       zram->table[index].ac_time = ktime_get_boottime();
+}
+
+static void zram_reset_access(struct zram *zram, u32 index)
+{
+       zram->table[index].ac_time = 0;
+}
+
+static ssize_t read_block_state(struct file *file, char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       char *kbuf;
+       ssize_t index, written = 0;
+       struct zram *zram = file->private_data;
+       unsigned long nr_pages = zram->disksize >> PAGE_SHIFT;
+       struct timespec64 ts;
+
+       kbuf = kvmalloc(count, GFP_KERNEL);
+       if (!kbuf)
+               return -ENOMEM;
+
+       down_read(&zram->init_lock);
+       if (!init_done(zram)) {
+               up_read(&zram->init_lock);
+               kvfree(kbuf);
+               return -EINVAL;
+       }
+
+       for (index = *ppos; index < nr_pages; index++) {
+               int copied;
+
+               zram_slot_lock(zram, index);
+               if (!zram_allocated(zram, index))
+                       goto next;
+
+               ts = ktime_to_timespec64(zram->table[index].ac_time);
+               copied = snprintf(kbuf + written, count,
+                       "%12zd %12lld.%06lu %c%c%c\n",
+                       index, (s64)ts.tv_sec,
+                       ts.tv_nsec / NSEC_PER_USEC,
+                       zram_test_flag(zram, index, ZRAM_SAME) ? 's' : '.',
+                       zram_test_flag(zram, index, ZRAM_WB) ? 'w' : '.',
+                       zram_test_flag(zram, index, ZRAM_HUGE) ? 'h' : '.');
+
+               if (count < copied) {
+                       zram_slot_unlock(zram, index);
+                       break;
+               }
+               written += copied;
+               count -= copied;
+next:
+               zram_slot_unlock(zram, index);
+               *ppos += 1;
+       }
+
+       up_read(&zram->init_lock);
+       if (copy_to_user(buf, kbuf, written))
+               written = -EFAULT;
+       kvfree(kbuf);
+
+       return written;
+}
+
+static const struct file_operations proc_zram_block_state_op = {
+       .open = simple_open,
+       .read = read_block_state,
+       .llseek = default_llseek,
+};
+
+static void zram_debugfs_register(struct zram *zram)
+{
+       if (!zram_debugfs_root)
+               return;
+
+       zram->debugfs_dir = debugfs_create_dir(zram->disk->disk_name,
+                                               zram_debugfs_root);
+       debugfs_create_file("block_state", 0400, zram->debugfs_dir,
+                               zram, &proc_zram_block_state_op);
+}
+
+static void zram_debugfs_unregister(struct zram *zram)
+{
+       debugfs_remove_recursive(zram->debugfs_dir);
+}
+#else
+static void zram_debugfs_create(void) {};
+static void zram_debugfs_destroy(void) {};
+static void zram_accessed(struct zram *zram, u32 index) {};
+static void zram_reset_access(struct zram *zram, u32 index) {};
+static void zram_debugfs_register(struct zram *zram) {};
+static void zram_debugfs_unregister(struct zram *zram) {};
+#endif
 
 /*
  * We switched to per-cpu streams and this attr is not needed anymore.
@@ -719,14 +845,15 @@ static ssize_t mm_stat_show(struct device *dev,
        max_used = atomic_long_read(&zram->stats.max_used_pages);
 
        ret = scnprintf(buf, PAGE_SIZE,
-                       "%8llu %8llu %8llu %8lu %8ld %8llu %8lu\n",
+                       "%8llu %8llu %8llu %8lu %8ld %8llu %8lu %8llu\n",
                        orig_size << PAGE_SHIFT,
                        (u64)atomic64_read(&zram->stats.compr_data_size),
                        mem_used << PAGE_SHIFT,
                        zram->limit_pages << PAGE_SHIFT,
                        max_used << PAGE_SHIFT,
                        (u64)atomic64_read(&zram->stats.same_pages),
-                       pool_stats.pages_compacted);
+                       pool_stats.pages_compacted,
+                       (u64)atomic64_read(&zram->stats.huge_pages));
        up_read(&zram->init_lock);
 
        return ret;
@@ -753,16 +880,6 @@ static DEVICE_ATTR_RO(io_stat);
 static DEVICE_ATTR_RO(mm_stat);
 static DEVICE_ATTR_RO(debug_stat);
 
-static void zram_slot_lock(struct zram *zram, u32 index)
-{
-       bit_spin_lock(ZRAM_ACCESS, &zram->table[index].value);
-}
-
-static void zram_slot_unlock(struct zram *zram, u32 index)
-{
-       bit_spin_unlock(ZRAM_ACCESS, &zram->table[index].value);
-}
-
 static void zram_meta_free(struct zram *zram, u64 disksize)
 {
        size_t num_pages = disksize >> PAGE_SHIFT;
@@ -805,6 +922,13 @@ static void zram_free_page(struct zram *zram, size_t index)
 {
        unsigned long handle;
 
+       zram_reset_access(zram, index);
+
+       if (zram_test_flag(zram, index, ZRAM_HUGE)) {
+               zram_clear_flag(zram, index, ZRAM_HUGE);
+               atomic64_dec(&zram->stats.huge_pages);
+       }
+
        if (zram_wb_enabled(zram) && zram_test_flag(zram, index, ZRAM_WB)) {
                zram_wb_clear(zram, index);
                atomic64_dec(&zram->stats.pages_stored);
@@ -973,6 +1097,7 @@ compress_again:
        }
 
        if (unlikely(comp_len >= huge_class_size)) {
+               comp_len = PAGE_SIZE;
                if (zram_wb_enabled(zram) && allow_wb) {
                        zcomp_stream_put(zram->comp);
                        ret = write_to_bdev(zram, bvec, index, bio, &element);
@@ -984,7 +1109,6 @@ compress_again:
                        allow_wb = false;
                        goto compress_again;
                }
-               comp_len = PAGE_SIZE;
        }
 
        /*
@@ -1046,6 +1170,11 @@ out:
        zram_slot_lock(zram, index);
        zram_free_page(zram, index);
 
+       if (comp_len == PAGE_SIZE) {
+               zram_set_flag(zram, index, ZRAM_HUGE);
+               atomic64_inc(&zram->stats.huge_pages);
+       }
+
        if (flags) {
                zram_set_flag(zram, index, flags);
                zram_set_element(zram, index, element);
@@ -1166,6 +1295,10 @@ static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index,
 
        generic_end_io_acct(q, rw_acct, &zram->disk->part0, start_time);
 
+       zram_slot_lock(zram, index);
+       zram_accessed(zram, index);
+       zram_slot_unlock(zram, index);
+
        if (unlikely(ret < 0)) {
                if (!is_write)
                        atomic64_inc(&zram->stats.failed_reads);
@@ -1577,6 +1710,7 @@ static int zram_add(void)
        }
        strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor));
 
+       zram_debugfs_register(zram);
        pr_info("Added device: %s\n", zram->disk->disk_name);
        return device_id;
 
@@ -1610,6 +1744,7 @@ static int zram_remove(struct zram *zram)
        zram->claim = true;
        mutex_unlock(&bdev->bd_mutex);
 
+       zram_debugfs_unregister(zram);
        /*
         * Remove sysfs first, so no one will perform a disksize
         * store while we destroy the devices. This also helps during
@@ -1712,6 +1847,7 @@ static void destroy_devices(void)
 {
        class_unregister(&zram_control_class);
        idr_for_each(&zram_index_idr, &zram_remove_cb, NULL);
+       zram_debugfs_destroy();
        idr_destroy(&zram_index_idr);
        unregister_blkdev(zram_major, "zram");
        cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE);
@@ -1733,6 +1869,7 @@ static int __init zram_init(void)
                return ret;
        }
 
+       zram_debugfs_create();
        zram_major = register_blkdev(0, "zram");
        if (zram_major <= 0) {
                pr_err("Unable to get major number\n");
index 008861220723cbb0da9338e390ff9f2bf3205818..72c8584b6dfff46847a73dbc066ceeff3ecbcec1 100644 (file)
 
 /* Flags for zram pages (table[page_no].value) */
 enum zram_pageflags {
-       /* Page consists the same element */
-       ZRAM_SAME = ZRAM_FLAG_SHIFT,
-       ZRAM_ACCESS,    /* page is now accessed */
+       /* zram slot is locked */
+       ZRAM_LOCK = ZRAM_FLAG_SHIFT,
+       ZRAM_SAME,      /* Page consists the same element */
        ZRAM_WB,        /* page is stored on backing_device */
+       ZRAM_HUGE,      /* Incompressible page */
 
        __NR_ZRAM_PAGEFLAGS,
 };
@@ -60,6 +61,9 @@ struct zram_table_entry {
                unsigned long element;
        };
        unsigned long value;
+#ifdef CONFIG_ZRAM_MEMORY_TRACKING
+       ktime_t ac_time;
+#endif
 };
 
 struct zram_stats {
@@ -71,6 +75,7 @@ struct zram_stats {
        atomic64_t invalid_io;  /* non-page-aligned I/O requests */
        atomic64_t notify_free; /* no. of swap slot free notifications */
        atomic64_t same_pages;          /* no. of same element filled pages */
+       atomic64_t huge_pages;          /* no. of huge pages */
        atomic64_t pages_stored;        /* no. of pages currently stored */
        atomic_long_t max_used_pages;   /* no. of maximum pages stored */
        atomic64_t writestall;          /* no. of write slow paths */
@@ -107,5 +112,8 @@ struct zram {
        unsigned long nr_pages;
        spinlock_t bitmap_lock;
 #endif
+#ifdef CONFIG_ZRAM_MEMORY_TRACKING
+       struct dentry *debugfs_dir;
+#endif
 };
 #endif
index e622f0f10502886556d637b2c5951efce78fcba0..0429c8ee58f1e32b0b925f40b9326a41f0622641 100644 (file)
@@ -210,12 +210,12 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
                                p9_debug(P9_DEBUG_ERROR,
                                         "integer field, but no integer?\n");
                                ret = r;
-                               continue;
-                       }
-                       v9ses->debug = option;
+                       } else {
+                               v9ses->debug = option;
 #ifdef CONFIG_NET_9P_DEBUG
-                       p9_debug_level = option;
+                               p9_debug_level = option;
 #endif
+                       }
                        break;
 
                case Opt_dfltuid:
@@ -231,7 +231,6 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
                                p9_debug(P9_DEBUG_ERROR,
                                         "uid field, but not a uid?\n");
                                ret = -EINVAL;
-                               continue;
                        }
                        break;
                case Opt_dfltgid:
@@ -247,7 +246,6 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
                                p9_debug(P9_DEBUG_ERROR,
                                         "gid field, but not a gid?\n");
                                ret = -EINVAL;
-                               continue;
                        }
                        break;
                case Opt_afid:
@@ -256,9 +254,9 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
                                p9_debug(P9_DEBUG_ERROR,
                                         "integer field, but no integer?\n");
                                ret = r;
-                               continue;
+                       } else {
+                               v9ses->afid = option;
                        }
-                       v9ses->afid = option;
                        break;
                case Opt_uname:
                        kfree(v9ses->uname);
@@ -306,13 +304,12 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
                                         "problem allocating copy of cache arg\n");
                                goto free_and_return;
                        }
-                       ret = get_cache_mode(s);
-                       if (ret == -EINVAL) {
-                               kfree(s);
-                               goto free_and_return;
-                       }
+                       r = get_cache_mode(s);
+                       if (r < 0)
+                               ret = r;
+                       else
+                               v9ses->cache = r;
 
-                       v9ses->cache = ret;
                        kfree(s);
                        break;
 
@@ -341,14 +338,12 @@ static int v9fs_parse_options(struct v9fs_session_info *v9ses, char *opts)
                                        pr_info("Unknown access argument %s\n",
                                                s);
                                        kfree(s);
-                                       goto free_and_return;
+                                       continue;
                                }
                                v9ses->uid = make_kuid(current_user_ns(), uid);
                                if (!uid_valid(v9ses->uid)) {
                                        ret = -EINVAL;
                                        pr_info("Uknown uid %s\n", s);
-                                       kfree(s);
-                                       goto free_and_return;
                                }
                        }
 
index ac4ac908f001a28f5af87f59be4fd6afe398ef23..40cdae75e3b4c929dae42a4a077b79d35f77ac33 100644 (file)
@@ -108,6 +108,7 @@ source "fs/notify/Kconfig"
 
 source "fs/quota/Kconfig"
 
+source "fs/autofs/Kconfig"
 source "fs/autofs4/Kconfig"
 source "fs/fuse/Kconfig"
 source "fs/overlayfs/Kconfig"
@@ -203,6 +204,9 @@ config HUGETLBFS
 config HUGETLB_PAGE
        def_bool HUGETLBFS
 
+config MEMFD_CREATE
+       def_bool TMPFS || HUGETLBFS
+
 config ARCH_HAS_GIGANTIC_PAGE
        bool
 
index c9375fd2c8c4d337162c77899f9b183bf3ff106e..2e005525cc19136e4d1149a51fa623393b385e78 100644 (file)
@@ -102,6 +102,7 @@ obj-$(CONFIG_AFFS_FS)               += affs/
 obj-$(CONFIG_ROMFS_FS)         += romfs/
 obj-$(CONFIG_QNX4FS_FS)                += qnx4/
 obj-$(CONFIG_QNX6FS_FS)                += qnx6/
+obj-$(CONFIG_AUTOFS_FS)                += autofs/
 obj-$(CONFIG_AUTOFS4_FS)       += autofs4/
 obj-$(CONFIG_ADFS_FS)          += adfs/
 obj-$(CONFIG_FUSE_FS)          += fuse/
diff --git a/fs/autofs/Kconfig b/fs/autofs/Kconfig
new file mode 100644 (file)
index 0000000..6a2064e
--- /dev/null
@@ -0,0 +1,20 @@
+config AUTOFS_FS
+       tristate "Kernel automounter support (supports v3, v4 and v5)"
+       default n
+       help
+          The automounter is a tool to automatically mount remote file systems
+          on demand. This implementation is partially kernel-based to reduce
+          overhead in the already-mounted case; this is unlike the BSD
+          automounter (amd), which is a pure user space daemon.
+
+          To use the automounter you need the user-space tools from
+          <https://www.kernel.org/pub/linux/daemons/autofs/>; you also want
+          to answer Y to "NFS file system support", below.
+
+          To compile this support as a module, choose M here: the module will be
+          called autofs.
+
+          If you are not a part of a fairly large, distributed network or
+          don't have a laptop which needs to dynamically reconfigure to the
+          local network, you probably do not need an automounter, and can say
+          N here.
diff --git a/fs/autofs/Makefile b/fs/autofs/Makefile
new file mode 100644 (file)
index 0000000..43fedde
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the linux autofs-filesystem routines.
+#
+
+obj-$(CONFIG_AUTOFS_FS) += autofs.o
+
+autofs-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o
diff --git a/fs/autofs/autofs_i.h b/fs/autofs/autofs_i.h
new file mode 100644 (file)
index 0000000..9400a9f
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *  Copyright 1997-1998 Transmeta Corporation - All Rights Reserved
+ *  Copyright 2005-2006 Ian Kent <raven@themaw.net>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+/* Internal header file for autofs */
+
+#include <linux/auto_fs.h>
+#include <linux/auto_dev-ioctl.h>
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/completion.h>
+#include <linux/file.h>
+
+/* This is the range of ioctl() numbers we claim as ours */
+#define AUTOFS_IOC_FIRST     AUTOFS_IOC_READY
+#define AUTOFS_IOC_COUNT     32
+
+#define AUTOFS_DEV_IOCTL_IOC_FIRST     (AUTOFS_DEV_IOCTL_VERSION)
+#define AUTOFS_DEV_IOCTL_IOC_COUNT \
+       (AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD - AUTOFS_DEV_IOCTL_VERSION_CMD)
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+#define pr_fmt(fmt) KBUILD_MODNAME ":pid:%d:%s: " fmt, current->pid, __func__
+
+/*
+ * Unified info structure.  This is pointed to by both the dentry and
+ * inode structures.  Each file in the filesystem has an instance of this
+ * structure.  It holds a reference to the dentry, so dentries are never
+ * flushed while the file exists.  All name lookups are dealt with at the
+ * dentry level, although the filesystem can interfere in the validation
+ * process.  Readdir is implemented by traversing the dentry lists.
+ */
+struct autofs_info {
+       struct dentry   *dentry;
+       struct inode    *inode;
+
+       int             flags;
+
+       struct completion expire_complete;
+
+       struct list_head active;
+       int active_count;
+
+       struct list_head expiring;
+
+       struct autofs_sb_info *sbi;
+       unsigned long last_used;
+       atomic_t count;
+
+       kuid_t uid;
+       kgid_t gid;
+};
+
+#define AUTOFS_INF_EXPIRING    (1<<0) /* dentry in the process of expiring */
+#define AUTOFS_INF_WANT_EXPIRE (1<<1) /* the dentry is being considered
+                                       * for expiry, so RCU_walk is
+                                       * not permitted.  If it progresses to
+                                       * actual expiry attempt, the flag is
+                                       * not cleared when EXPIRING is set -
+                                       * in that case it gets cleared only
+                                       * when it comes to clearing EXPIRING.
+                                       */
+#define AUTOFS_INF_PENDING     (1<<2) /* dentry pending mount */
+
+struct autofs_wait_queue {
+       wait_queue_head_t queue;
+       struct autofs_wait_queue *next;
+       autofs_wqt_t wait_queue_token;
+       /* We use the following to see what we are waiting for */
+       struct qstr name;
+       u32 dev;
+       u64 ino;
+       kuid_t uid;
+       kgid_t gid;
+       pid_t pid;
+       pid_t tgid;
+       /* This is for status reporting upon return */
+       int status;
+       unsigned int wait_ctr;
+};
+
+#define AUTOFS_SBI_MAGIC 0x6d4a556d
+
+struct autofs_sb_info {
+       u32 magic;
+       int pipefd;
+       struct file *pipe;
+       struct pid *oz_pgrp;
+       int catatonic;
+       int version;
+       int sub_version;
+       int min_proto;
+       int max_proto;
+       unsigned long exp_timeout;
+       unsigned int type;
+       struct super_block *sb;
+       struct mutex wq_mutex;
+       struct mutex pipe_mutex;
+       spinlock_t fs_lock;
+       struct autofs_wait_queue *queues; /* Wait queue pointer */
+       spinlock_t lookup_lock;
+       struct list_head active_list;
+       struct list_head expiring_list;
+       struct rcu_head rcu;
+};
+
+static inline struct autofs_sb_info *autofs_sbi(struct super_block *sb)
+{
+       return (struct autofs_sb_info *)(sb->s_fs_info);
+}
+
+static inline struct autofs_info *autofs_dentry_ino(struct dentry *dentry)
+{
+       return (struct autofs_info *)(dentry->d_fsdata);
+}
+
+/* autofs_oz_mode(): do we see the man behind the curtain?  (The
+ * processes which do manipulations for us in user space sees the raw
+ * filesystem without "magic".)
+ */
+static inline int autofs_oz_mode(struct autofs_sb_info *sbi)
+{
+       return sbi->catatonic || task_pgrp(current) == sbi->oz_pgrp;
+}
+
+struct inode *autofs_get_inode(struct super_block *, umode_t);
+void autofs_free_ino(struct autofs_info *);
+
+/* Expiration */
+int is_autofs_dentry(struct dentry *);
+int autofs_expire_wait(const struct path *path, int rcu_walk);
+int autofs_expire_run(struct super_block *, struct vfsmount *,
+                     struct autofs_sb_info *,
+                     struct autofs_packet_expire __user *);
+int autofs_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
+                          struct autofs_sb_info *sbi, int when);
+int autofs_expire_multi(struct super_block *, struct vfsmount *,
+                       struct autofs_sb_info *, int __user *);
+struct dentry *autofs_expire_direct(struct super_block *sb,
+                                   struct vfsmount *mnt,
+                                   struct autofs_sb_info *sbi, int how);
+struct dentry *autofs_expire_indirect(struct super_block *sb,
+                                     struct vfsmount *mnt,
+                                     struct autofs_sb_info *sbi, int how);
+
+/* Device node initialization */
+
+int autofs_dev_ioctl_init(void);
+void autofs_dev_ioctl_exit(void);
+
+/* Operations structures */
+
+extern const struct inode_operations autofs_symlink_inode_operations;
+extern const struct inode_operations autofs_dir_inode_operations;
+extern const struct file_operations autofs_dir_operations;
+extern const struct file_operations autofs_root_operations;
+extern const struct dentry_operations autofs_dentry_operations;
+
+/* VFS automount flags management functions */
+static inline void __managed_dentry_set_managed(struct dentry *dentry)
+{
+       dentry->d_flags |= (DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT);
+}
+
+static inline void managed_dentry_set_managed(struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       __managed_dentry_set_managed(dentry);
+       spin_unlock(&dentry->d_lock);
+}
+
+static inline void __managed_dentry_clear_managed(struct dentry *dentry)
+{
+       dentry->d_flags &= ~(DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT);
+}
+
+static inline void managed_dentry_clear_managed(struct dentry *dentry)
+{
+       spin_lock(&dentry->d_lock);
+       __managed_dentry_clear_managed(dentry);
+       spin_unlock(&dentry->d_lock);
+}
+
+/* Initializing function */
+
+int autofs_fill_super(struct super_block *, void *, int);
+struct autofs_info *autofs_new_ino(struct autofs_sb_info *);
+void autofs_clean_ino(struct autofs_info *);
+
+static inline int autofs_prepare_pipe(struct file *pipe)
+{
+       if (!(pipe->f_mode & FMODE_CAN_WRITE))
+               return -EINVAL;
+       if (!S_ISFIFO(file_inode(pipe)->i_mode))
+               return -EINVAL;
+       /* We want a packet pipe */
+       pipe->f_flags |= O_DIRECT;
+       return 0;
+}
+
+/* Queue management functions */
+
+int autofs_wait(struct autofs_sb_info *,
+                const struct path *, enum autofs_notify);
+int autofs_wait_release(struct autofs_sb_info *, autofs_wqt_t, int);
+void autofs_catatonic_mode(struct autofs_sb_info *);
+
+static inline u32 autofs_get_dev(struct autofs_sb_info *sbi)
+{
+       return new_encode_dev(sbi->sb->s_dev);
+}
+
+static inline u64 autofs_get_ino(struct autofs_sb_info *sbi)
+{
+       return d_inode(sbi->sb->s_root)->i_ino;
+}
+
+static inline void __autofs_add_expiring(struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+
+       if (ino) {
+               if (list_empty(&ino->expiring))
+                       list_add(&ino->expiring, &sbi->expiring_list);
+       }
+}
+
+static inline void autofs_add_expiring(struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+
+       if (ino) {
+               spin_lock(&sbi->lookup_lock);
+               if (list_empty(&ino->expiring))
+                       list_add(&ino->expiring, &sbi->expiring_list);
+               spin_unlock(&sbi->lookup_lock);
+       }
+}
+
+static inline void autofs_del_expiring(struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+
+       if (ino) {
+               spin_lock(&sbi->lookup_lock);
+               if (!list_empty(&ino->expiring))
+                       list_del_init(&ino->expiring);
+               spin_unlock(&sbi->lookup_lock);
+       }
+}
+
+void autofs_kill_sb(struct super_block *);
diff --git a/fs/autofs/dev-ioctl.c b/fs/autofs/dev-ioctl.c
new file mode 100644 (file)
index 0000000..ea4ca14
--- /dev/null
@@ -0,0 +1,748 @@
+/*
+ * Copyright 2008 Red Hat, Inc. All rights reserved.
+ * Copyright 2008 Ian Kent <raven@themaw.net>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+#include <linux/miscdevice.h>
+#include <linux/compat.h>
+#include <linux/syscalls.h>
+#include <linux/magic.h>
+
+#include "autofs_i.h"
+
+/*
+ * This module implements an interface for routing autofs ioctl control
+ * commands via a miscellaneous device file.
+ *
+ * The alternate interface is needed because we need to be able open
+ * an ioctl file descriptor on an autofs mount that may be covered by
+ * another mount. This situation arises when starting automount(8)
+ * or other user space daemon which uses direct mounts or offset
+ * mounts (used for autofs lazy mount/umount of nested mount trees),
+ * which have been left busy at at service shutdown.
+ */
+
+typedef int (*ioctl_fn)(struct file *, struct autofs_sb_info *,
+                       struct autofs_dev_ioctl *);
+
+static int check_name(const char *name)
+{
+       if (!strchr(name, '/'))
+               return -EINVAL;
+       return 0;
+}
+
+/*
+ * Check a string doesn't overrun the chunk of
+ * memory we copied from user land.
+ */
+static int invalid_str(char *str, size_t size)
+{
+       if (memchr(str, 0, size))
+               return 0;
+       return -EINVAL;
+}
+
+/*
+ * Check that the user compiled against correct version of autofs
+ * misc device code.
+ *
+ * As well as checking the version compatibility this always copies
+ * the kernel interface version out.
+ */
+static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param)
+{
+       int err = 0;
+
+       if ((param->ver_major != AUTOFS_DEV_IOCTL_VERSION_MAJOR) ||
+           (param->ver_minor > AUTOFS_DEV_IOCTL_VERSION_MINOR)) {
+               pr_warn("ioctl control interface version mismatch: "
+                       "kernel(%u.%u), user(%u.%u), cmd(0x%08x)\n",
+                       AUTOFS_DEV_IOCTL_VERSION_MAJOR,
+                       AUTOFS_DEV_IOCTL_VERSION_MINOR,
+                       param->ver_major, param->ver_minor, cmd);
+               err = -EINVAL;
+       }
+
+       /* Fill in the kernel version. */
+       param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR;
+       param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR;
+
+       return err;
+}
+
+/*
+ * Copy parameter control struct, including a possible path allocated
+ * at the end of the struct.
+ */
+static struct autofs_dev_ioctl *
+copy_dev_ioctl(struct autofs_dev_ioctl __user *in)
+{
+       struct autofs_dev_ioctl tmp, *res;
+
+       if (copy_from_user(&tmp, in, AUTOFS_DEV_IOCTL_SIZE))
+               return ERR_PTR(-EFAULT);
+
+       if (tmp.size < AUTOFS_DEV_IOCTL_SIZE)
+               return ERR_PTR(-EINVAL);
+
+       if (tmp.size > AUTOFS_DEV_IOCTL_SIZE + PATH_MAX)
+               return ERR_PTR(-ENAMETOOLONG);
+
+       res = memdup_user(in, tmp.size);
+       if (!IS_ERR(res))
+               res->size = tmp.size;
+
+       return res;
+}
+
+static inline void free_dev_ioctl(struct autofs_dev_ioctl *param)
+{
+       kfree(param);
+}
+
+/*
+ * Check sanity of parameter control fields and if a path is present
+ * check that it is terminated and contains at least one "/".
+ */
+static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param)
+{
+       int err;
+
+       err = check_dev_ioctl_version(cmd, param);
+       if (err) {
+               pr_warn("invalid device control module version "
+                       "supplied for cmd(0x%08x)\n", cmd);
+               goto out;
+       }
+
+       if (param->size > AUTOFS_DEV_IOCTL_SIZE) {
+               err = invalid_str(param->path, param->size - AUTOFS_DEV_IOCTL_SIZE);
+               if (err) {
+                       pr_warn(
+                         "path string terminator missing for cmd(0x%08x)\n",
+                         cmd);
+                       goto out;
+               }
+
+               err = check_name(param->path);
+               if (err) {
+                       pr_warn("invalid path supplied for cmd(0x%08x)\n",
+                               cmd);
+                       goto out;
+               }
+       }
+
+       err = 0;
+out:
+       return err;
+}
+
+/*
+ * Get the autofs super block info struct from the file opened on
+ * the autofs mount point.
+ */
+static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f)
+{
+       struct autofs_sb_info *sbi = NULL;
+       struct inode *inode;
+
+       if (f) {
+               inode = file_inode(f);
+               sbi = autofs_sbi(inode->i_sb);
+       }
+       return sbi;
+}
+
+/* Return autofs dev ioctl version */
+static int autofs_dev_ioctl_version(struct file *fp,
+                                   struct autofs_sb_info *sbi,
+                                   struct autofs_dev_ioctl *param)
+{
+       /* This should have already been set. */
+       param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR;
+       param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR;
+       return 0;
+}
+
+/* Return autofs module protocol version */
+static int autofs_dev_ioctl_protover(struct file *fp,
+                                    struct autofs_sb_info *sbi,
+                                    struct autofs_dev_ioctl *param)
+{
+       param->protover.version = sbi->version;
+       return 0;
+}
+
+/* Return autofs module protocol sub version */
+static int autofs_dev_ioctl_protosubver(struct file *fp,
+                                       struct autofs_sb_info *sbi,
+                                       struct autofs_dev_ioctl *param)
+{
+       param->protosubver.sub_version = sbi->sub_version;
+       return 0;
+}
+
+/* Find the topmost mount satisfying test() */
+static int find_autofs_mount(const char *pathname,
+                            struct path *res,
+                            int test(const struct path *path, void *data),
+                            void *data)
+{
+       struct path path;
+       int err;
+
+       err = kern_path_mountpoint(AT_FDCWD, pathname, &path, 0);
+       if (err)
+               return err;
+       err = -ENOENT;
+       while (path.dentry == path.mnt->mnt_root) {
+               if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) {
+                       if (test(&path, data)) {
+                               path_get(&path);
+                               *res = path;
+                               err = 0;
+                               break;
+                       }
+               }
+               if (!follow_up(&path))
+                       break;
+       }
+       path_put(&path);
+       return err;
+}
+
+static int test_by_dev(const struct path *path, void *p)
+{
+       return path->dentry->d_sb->s_dev == *(dev_t *)p;
+}
+
+static int test_by_type(const struct path *path, void *p)
+{
+       struct autofs_info *ino = autofs_dentry_ino(path->dentry);
+
+       return ino && ino->sbi->type & *(unsigned *)p;
+}
+
+/*
+ * Open a file descriptor on the autofs mount point corresponding
+ * to the given path and device number (aka. new_encode_dev(sb->s_dev)).
+ */
+static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid)
+{
+       int err, fd;
+
+       fd = get_unused_fd_flags(O_CLOEXEC);
+       if (likely(fd >= 0)) {
+               struct file *filp;
+               struct path path;
+
+               err = find_autofs_mount(name, &path, test_by_dev, &devid);
+               if (err)
+                       goto out;
+
+               filp = dentry_open(&path, O_RDONLY, current_cred());
+               path_put(&path);
+               if (IS_ERR(filp)) {
+                       err = PTR_ERR(filp);
+                       goto out;
+               }
+
+               fd_install(fd, filp);
+       }
+
+       return fd;
+
+out:
+       put_unused_fd(fd);
+       return err;
+}
+
+/* Open a file descriptor on an autofs mount point */
+static int autofs_dev_ioctl_openmount(struct file *fp,
+                                     struct autofs_sb_info *sbi,
+                                     struct autofs_dev_ioctl *param)
+{
+       const char *path;
+       dev_t devid;
+       int err, fd;
+
+       /* param->path has already been checked */
+       if (!param->openmount.devid)
+               return -EINVAL;
+
+       param->ioctlfd = -1;
+
+       path = param->path;
+       devid = new_decode_dev(param->openmount.devid);
+
+       err = 0;
+       fd = autofs_dev_ioctl_open_mountpoint(path, devid);
+       if (unlikely(fd < 0)) {
+               err = fd;
+               goto out;
+       }
+
+       param->ioctlfd = fd;
+out:
+       return err;
+}
+
+/* Close file descriptor allocated above (user can also use close(2)). */
+static int autofs_dev_ioctl_closemount(struct file *fp,
+                                      struct autofs_sb_info *sbi,
+                                      struct autofs_dev_ioctl *param)
+{
+       return ksys_close(param->ioctlfd);
+}
+
+/*
+ * Send "ready" status for an existing wait (either a mount or an expire
+ * request).
+ */
+static int autofs_dev_ioctl_ready(struct file *fp,
+                                 struct autofs_sb_info *sbi,
+                                 struct autofs_dev_ioctl *param)
+{
+       autofs_wqt_t token;
+
+       token = (autofs_wqt_t) param->ready.token;
+       return autofs_wait_release(sbi, token, 0);
+}
+
+/*
+ * Send "fail" status for an existing wait (either a mount or an expire
+ * request).
+ */
+static int autofs_dev_ioctl_fail(struct file *fp,
+                                struct autofs_sb_info *sbi,
+                                struct autofs_dev_ioctl *param)
+{
+       autofs_wqt_t token;
+       int status;
+
+       token = (autofs_wqt_t) param->fail.token;
+       status = param->fail.status < 0 ? param->fail.status : -ENOENT;
+       return autofs_wait_release(sbi, token, status);
+}
+
+/*
+ * Set the pipe fd for kernel communication to the daemon.
+ *
+ * Normally this is set at mount using an option but if we
+ * are reconnecting to a busy mount then we need to use this
+ * to tell the autofs mount about the new kernel pipe fd. In
+ * order to protect mounts against incorrectly setting the
+ * pipefd we also require that the autofs mount be catatonic.
+ *
+ * This also sets the process group id used to identify the
+ * controlling process (eg. the owning automount(8) daemon).
+ */
+static int autofs_dev_ioctl_setpipefd(struct file *fp,
+                                     struct autofs_sb_info *sbi,
+                                     struct autofs_dev_ioctl *param)
+{
+       int pipefd;
+       int err = 0;
+       struct pid *new_pid = NULL;
+
+       if (param->setpipefd.pipefd == -1)
+               return -EINVAL;
+
+       pipefd = param->setpipefd.pipefd;
+
+       mutex_lock(&sbi->wq_mutex);
+       if (!sbi->catatonic) {
+               mutex_unlock(&sbi->wq_mutex);
+               return -EBUSY;
+       } else {
+               struct file *pipe;
+
+               new_pid = get_task_pid(current, PIDTYPE_PGID);
+
+               if (ns_of_pid(new_pid) != ns_of_pid(sbi->oz_pgrp)) {
+                       pr_warn("not allowed to change PID namespace\n");
+                       err = -EINVAL;
+                       goto out;
+               }
+
+               pipe = fget(pipefd);
+               if (!pipe) {
+                       err = -EBADF;
+                       goto out;
+               }
+               if (autofs_prepare_pipe(pipe) < 0) {
+                       err = -EPIPE;
+                       fput(pipe);
+                       goto out;
+               }
+               swap(sbi->oz_pgrp, new_pid);
+               sbi->pipefd = pipefd;
+               sbi->pipe = pipe;
+               sbi->catatonic = 0;
+       }
+out:
+       put_pid(new_pid);
+       mutex_unlock(&sbi->wq_mutex);
+       return err;
+}
+
+/*
+ * Make the autofs mount point catatonic, no longer responsive to
+ * mount requests. Also closes the kernel pipe file descriptor.
+ */
+static int autofs_dev_ioctl_catatonic(struct file *fp,
+                                     struct autofs_sb_info *sbi,
+                                     struct autofs_dev_ioctl *param)
+{
+       autofs_catatonic_mode(sbi);
+       return 0;
+}
+
+/* Set the autofs mount timeout */
+static int autofs_dev_ioctl_timeout(struct file *fp,
+                                   struct autofs_sb_info *sbi,
+                                   struct autofs_dev_ioctl *param)
+{
+       unsigned long timeout;
+
+       timeout = param->timeout.timeout;
+       param->timeout.timeout = sbi->exp_timeout / HZ;
+       sbi->exp_timeout = timeout * HZ;
+       return 0;
+}
+
+/*
+ * Return the uid and gid of the last request for the mount
+ *
+ * When reconstructing an autofs mount tree with active mounts
+ * we need to re-connect to mounts that may have used the original
+ * process uid and gid (or string variations of them) for mount
+ * lookups within the map entry.
+ */
+static int autofs_dev_ioctl_requester(struct file *fp,
+                                     struct autofs_sb_info *sbi,
+                                     struct autofs_dev_ioctl *param)
+{
+       struct autofs_info *ino;
+       struct path path;
+       dev_t devid;
+       int err = -ENOENT;
+
+       if (param->size <= AUTOFS_DEV_IOCTL_SIZE) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       devid = sbi->sb->s_dev;
+
+       param->requester.uid = param->requester.gid = -1;
+
+       err = find_autofs_mount(param->path, &path, test_by_dev, &devid);
+       if (err)
+               goto out;
+
+       ino = autofs_dentry_ino(path.dentry);
+       if (ino) {
+               err = 0;
+               autofs_expire_wait(&path, 0);
+               spin_lock(&sbi->fs_lock);
+               param->requester.uid =
+                       from_kuid_munged(current_user_ns(), ino->uid);
+               param->requester.gid =
+                       from_kgid_munged(current_user_ns(), ino->gid);
+               spin_unlock(&sbi->fs_lock);
+       }
+       path_put(&path);
+out:
+       return err;
+}
+
+/*
+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing
+ * more that can be done.
+ */
+static int autofs_dev_ioctl_expire(struct file *fp,
+                                  struct autofs_sb_info *sbi,
+                                  struct autofs_dev_ioctl *param)
+{
+       struct vfsmount *mnt;
+       int how;
+
+       how = param->expire.how;
+       mnt = fp->f_path.mnt;
+
+       return autofs_do_expire_multi(sbi->sb, mnt, sbi, how);
+}
+
+/* Check if autofs mount point is in use */
+static int autofs_dev_ioctl_askumount(struct file *fp,
+                                     struct autofs_sb_info *sbi,
+                                     struct autofs_dev_ioctl *param)
+{
+       param->askumount.may_umount = 0;
+       if (may_umount(fp->f_path.mnt))
+               param->askumount.may_umount = 1;
+       return 0;
+}
+
+/*
+ * Check if the given path is a mountpoint.
+ *
+ * If we are supplied with the file descriptor of an autofs
+ * mount we're looking for a specific mount. In this case
+ * the path is considered a mountpoint if it is itself a
+ * mountpoint or contains a mount, such as a multi-mount
+ * without a root mount. In this case we return 1 if the
+ * path is a mount point and the super magic of the covering
+ * mount if there is one or 0 if it isn't a mountpoint.
+ *
+ * If we aren't supplied with a file descriptor then we
+ * lookup the path and check if it is the root of a mount.
+ * If a type is given we are looking for a particular autofs
+ * mount and if we don't find a match we return fail. If the
+ * located path is the root of a mount we return 1 along with
+ * the super magic of the mount or 0 otherwise.
+ *
+ * In both cases the the device number (as returned by
+ * new_encode_dev()) is also returned.
+ */
+static int autofs_dev_ioctl_ismountpoint(struct file *fp,
+                                        struct autofs_sb_info *sbi,
+                                        struct autofs_dev_ioctl *param)
+{
+       struct path path;
+       const char *name;
+       unsigned int type;
+       unsigned int devid, magic;
+       int err = -ENOENT;
+
+       if (param->size <= AUTOFS_DEV_IOCTL_SIZE) {
+               err = -EINVAL;
+               goto out;
+       }
+
+       name = param->path;
+       type = param->ismountpoint.in.type;
+
+       param->ismountpoint.out.devid = devid = 0;
+       param->ismountpoint.out.magic = magic = 0;
+
+       if (!fp || param->ioctlfd == -1) {
+               if (autofs_type_any(type))
+                       err = kern_path_mountpoint(AT_FDCWD,
+                                                  name, &path, LOOKUP_FOLLOW);
+               else
+                       err = find_autofs_mount(name, &path,
+                                               test_by_type, &type);
+               if (err)
+                       goto out;
+               devid = new_encode_dev(path.dentry->d_sb->s_dev);
+               err = 0;
+               if (path.mnt->mnt_root == path.dentry) {
+                       err = 1;
+                       magic = path.dentry->d_sb->s_magic;
+               }
+       } else {
+               dev_t dev = sbi->sb->s_dev;
+
+               err = find_autofs_mount(name, &path, test_by_dev, &dev);
+               if (err)
+                       goto out;
+
+               devid = new_encode_dev(dev);
+
+               err = path_has_submounts(&path);
+
+               if (follow_down_one(&path))
+                       magic = path.dentry->d_sb->s_magic;
+       }
+
+       param->ismountpoint.out.devid = devid;
+       param->ismountpoint.out.magic = magic;
+       path_put(&path);
+out:
+       return err;
+}
+
+/*
+ * Our range of ioctl numbers isn't 0 based so we need to shift
+ * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table
+ * lookup.
+ */
+#define cmd_idx(cmd)   (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST))
+
+static ioctl_fn lookup_dev_ioctl(unsigned int cmd)
+{
+       static ioctl_fn _ioctls[] = {
+               autofs_dev_ioctl_version,
+               autofs_dev_ioctl_protover,
+               autofs_dev_ioctl_protosubver,
+               autofs_dev_ioctl_openmount,
+               autofs_dev_ioctl_closemount,
+               autofs_dev_ioctl_ready,
+               autofs_dev_ioctl_fail,
+               autofs_dev_ioctl_setpipefd,
+               autofs_dev_ioctl_catatonic,
+               autofs_dev_ioctl_timeout,
+               autofs_dev_ioctl_requester,
+               autofs_dev_ioctl_expire,
+               autofs_dev_ioctl_askumount,
+               autofs_dev_ioctl_ismountpoint,
+       };
+       unsigned int idx = cmd_idx(cmd);
+
+       return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx];
+}
+
+/* ioctl dispatcher */
+static int _autofs_dev_ioctl(unsigned int command,
+                            struct autofs_dev_ioctl __user *user)
+{
+       struct autofs_dev_ioctl *param;
+       struct file *fp;
+       struct autofs_sb_info *sbi;
+       unsigned int cmd_first, cmd;
+       ioctl_fn fn = NULL;
+       int err = 0;
+
+       cmd_first = _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST);
+       cmd = _IOC_NR(command);
+
+       if (_IOC_TYPE(command) != _IOC_TYPE(AUTOFS_DEV_IOCTL_IOC_FIRST) ||
+           cmd - cmd_first > AUTOFS_DEV_IOCTL_IOC_COUNT) {
+               return -ENOTTY;
+       }
+
+       /* Only root can use ioctls other than AUTOFS_DEV_IOCTL_VERSION_CMD
+        * and AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD
+        */
+       if (cmd != AUTOFS_DEV_IOCTL_VERSION_CMD &&
+           cmd != AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD &&
+           !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       /* Copy the parameters into kernel space. */
+       param = copy_dev_ioctl(user);
+       if (IS_ERR(param))
+               return PTR_ERR(param);
+
+       err = validate_dev_ioctl(command, param);
+       if (err)
+               goto out;
+
+       fn = lookup_dev_ioctl(cmd);
+       if (!fn) {
+               pr_warn("unknown command 0x%08x\n", command);
+               err = -ENOTTY;
+               goto out;
+       }
+
+       fp = NULL;
+       sbi = NULL;
+
+       /*
+        * For obvious reasons the openmount can't have a file
+        * descriptor yet. We don't take a reference to the
+        * file during close to allow for immediate release,
+        * and the same for retrieving ioctl version.
+        */
+       if (cmd != AUTOFS_DEV_IOCTL_VERSION_CMD &&
+           cmd != AUTOFS_DEV_IOCTL_OPENMOUNT_CMD &&
+           cmd != AUTOFS_DEV_IOCTL_CLOSEMOUNT_CMD) {
+               fp = fget(param->ioctlfd);
+               if (!fp) {
+                       if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD)
+                               goto cont;
+                       err = -EBADF;
+                       goto out;
+               }
+
+               sbi = autofs_dev_ioctl_sbi(fp);
+               if (!sbi || sbi->magic != AUTOFS_SBI_MAGIC) {
+                       err = -EINVAL;
+                       fput(fp);
+                       goto out;
+               }
+
+               /*
+                * Admin needs to be able to set the mount catatonic in
+                * order to be able to perform the re-open.
+                */
+               if (!autofs_oz_mode(sbi) &&
+                   cmd != AUTOFS_DEV_IOCTL_CATATONIC_CMD) {
+                       err = -EACCES;
+                       fput(fp);
+                       goto out;
+               }
+       }
+cont:
+       err = fn(fp, sbi, param);
+
+       if (fp)
+               fput(fp);
+       if (err >= 0 && copy_to_user(user, param, AUTOFS_DEV_IOCTL_SIZE))
+               err = -EFAULT;
+out:
+       free_dev_ioctl(param);
+       return err;
+}
+
+static long autofs_dev_ioctl(struct file *file, unsigned int command,
+                            unsigned long u)
+{
+       int err;
+
+       err = _autofs_dev_ioctl(command, (struct autofs_dev_ioctl __user *) u);
+       return (long) err;
+}
+
+#ifdef CONFIG_COMPAT
+static long autofs_dev_ioctl_compat(struct file *file, unsigned int command,
+                                   unsigned long u)
+{
+       return autofs_dev_ioctl(file, command, (unsigned long) compat_ptr(u));
+}
+#else
+#define autofs_dev_ioctl_compat NULL
+#endif
+
+static const struct file_operations _dev_ioctl_fops = {
+       .unlocked_ioctl  = autofs_dev_ioctl,
+       .compat_ioctl = autofs_dev_ioctl_compat,
+       .owner   = THIS_MODULE,
+       .llseek = noop_llseek,
+};
+
+static struct miscdevice _autofs_dev_ioctl_misc = {
+       .minor          = AUTOFS_MINOR,
+       .name           = AUTOFS_DEVICE_NAME,
+       .fops           = &_dev_ioctl_fops,
+       .mode           = 0644,
+};
+
+MODULE_ALIAS_MISCDEV(AUTOFS_MINOR);
+MODULE_ALIAS("devname:autofs");
+
+/* Register/deregister misc character device */
+int __init autofs_dev_ioctl_init(void)
+{
+       int r;
+
+       r = misc_register(&_autofs_dev_ioctl_misc);
+       if (r) {
+               pr_err("misc_register failed for control device\n");
+               return r;
+       }
+
+       return 0;
+}
+
+void autofs_dev_ioctl_exit(void)
+{
+       misc_deregister(&_autofs_dev_ioctl_misc);
+}
diff --git a/fs/autofs/expire.c b/fs/autofs/expire.c
new file mode 100644 (file)
index 0000000..b332d3f
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
+ * Copyright 2001-2006 Ian Kent <raven@themaw.net>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+#include "autofs_i.h"
+
+static unsigned long now;
+
+/* Check if a dentry can be expired */
+static inline int autofs_can_expire(struct dentry *dentry,
+                                   unsigned long timeout, int do_now)
+{
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+
+       /* dentry in the process of being deleted */
+       if (ino == NULL)
+               return 0;
+
+       if (!do_now) {
+               /* Too young to die */
+               if (!timeout || time_after(ino->last_used + timeout, now))
+                       return 0;
+       }
+       return 1;
+}
+
+/* Check a mount point for busyness */
+static int autofs_mount_busy(struct vfsmount *mnt, struct dentry *dentry)
+{
+       struct dentry *top = dentry;
+       struct path path = {.mnt = mnt, .dentry = dentry};
+       int status = 1;
+
+       pr_debug("dentry %p %pd\n", dentry, dentry);
+
+       path_get(&path);
+
+       if (!follow_down_one(&path))
+               goto done;
+
+       if (is_autofs_dentry(path.dentry)) {
+               struct autofs_sb_info *sbi = autofs_sbi(path.dentry->d_sb);
+
+               /* This is an autofs submount, we can't expire it */
+               if (autofs_type_indirect(sbi->type))
+                       goto done;
+       }
+
+       /* Update the expiry counter if fs is busy */
+       if (!may_umount_tree(path.mnt)) {
+               struct autofs_info *ino;
+
+               ino = autofs_dentry_ino(top);
+               ino->last_used = jiffies;
+               goto done;
+       }
+
+       status = 0;
+done:
+       pr_debug("returning = %d\n", status);
+       path_put(&path);
+       return status;
+}
+
+/*
+ * Calculate and dget next entry in the subdirs list under root.
+ */
+static struct dentry *get_next_positive_subdir(struct dentry *prev,
+                                              struct dentry *root)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(root->d_sb);
+       struct list_head *next;
+       struct dentry *q;
+
+       spin_lock(&sbi->lookup_lock);
+       spin_lock(&root->d_lock);
+
+       if (prev)
+               next = prev->d_child.next;
+       else {
+               prev = dget_dlock(root);
+               next = prev->d_subdirs.next;
+       }
+
+cont:
+       if (next == &root->d_subdirs) {
+               spin_unlock(&root->d_lock);
+               spin_unlock(&sbi->lookup_lock);
+               dput(prev);
+               return NULL;
+       }
+
+       q = list_entry(next, struct dentry, d_child);
+
+       spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED);
+       /* Already gone or negative dentry (under construction) - try next */
+       if (!d_count(q) || !simple_positive(q)) {
+               spin_unlock(&q->d_lock);
+               next = q->d_child.next;
+               goto cont;
+       }
+       dget_dlock(q);
+       spin_unlock(&q->d_lock);
+       spin_unlock(&root->d_lock);
+       spin_unlock(&sbi->lookup_lock);
+
+       dput(prev);
+
+       return q;
+}
+
+/*
+ * Calculate and dget next entry in top down tree traversal.
+ */
+static struct dentry *get_next_positive_dentry(struct dentry *prev,
+                                              struct dentry *root)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(root->d_sb);
+       struct list_head *next;
+       struct dentry *p, *ret;
+
+       if (prev == NULL)
+               return dget(root);
+
+       spin_lock(&sbi->lookup_lock);
+relock:
+       p = prev;
+       spin_lock(&p->d_lock);
+again:
+       next = p->d_subdirs.next;
+       if (next == &p->d_subdirs) {
+               while (1) {
+                       struct dentry *parent;
+
+                       if (p == root) {
+                               spin_unlock(&p->d_lock);
+                               spin_unlock(&sbi->lookup_lock);
+                               dput(prev);
+                               return NULL;
+                       }
+
+                       parent = p->d_parent;
+                       if (!spin_trylock(&parent->d_lock)) {
+                               spin_unlock(&p->d_lock);
+                               cpu_relax();
+                               goto relock;
+                       }
+                       spin_unlock(&p->d_lock);
+                       next = p->d_child.next;
+                       p = parent;
+                       if (next != &parent->d_subdirs)
+                               break;
+               }
+       }
+       ret = list_entry(next, struct dentry, d_child);
+
+       spin_lock_nested(&ret->d_lock, DENTRY_D_LOCK_NESTED);
+       /* Negative dentry - try next */
+       if (!simple_positive(ret)) {
+               spin_unlock(&p->d_lock);
+               lock_set_subclass(&ret->d_lock.dep_map, 0, _RET_IP_);
+               p = ret;
+               goto again;
+       }
+       dget_dlock(ret);
+       spin_unlock(&ret->d_lock);
+       spin_unlock(&p->d_lock);
+       spin_unlock(&sbi->lookup_lock);
+
+       dput(prev);
+
+       return ret;
+}
+
+/*
+ * Check a direct mount point for busyness.
+ * Direct mounts have similar expiry semantics to tree mounts.
+ * The tree is not busy iff no mountpoints are busy and there are no
+ * autofs submounts.
+ */
+static int autofs_direct_busy(struct vfsmount *mnt,
+                             struct dentry *top,
+                             unsigned long timeout,
+                             int do_now)
+{
+       pr_debug("top %p %pd\n", top, top);
+
+       /* If it's busy update the expiry counters */
+       if (!may_umount_tree(mnt)) {
+               struct autofs_info *ino;
+
+               ino = autofs_dentry_ino(top);
+               if (ino)
+                       ino->last_used = jiffies;
+               return 1;
+       }
+
+       /* Timeout of a direct mount is determined by its top dentry */
+       if (!autofs_can_expire(top, timeout, do_now))
+               return 1;
+
+       return 0;
+}
+
+/*
+ * Check a directory tree of mount points for busyness
+ * The tree is not busy iff no mountpoints are busy
+ */
+static int autofs_tree_busy(struct vfsmount *mnt,
+                           struct dentry *top,
+                           unsigned long timeout,
+                           int do_now)
+{
+       struct autofs_info *top_ino = autofs_dentry_ino(top);
+       struct dentry *p;
+
+       pr_debug("top %p %pd\n", top, top);
+
+       /* Negative dentry - give up */
+       if (!simple_positive(top))
+               return 1;
+
+       p = NULL;
+       while ((p = get_next_positive_dentry(p, top))) {
+               pr_debug("dentry %p %pd\n", p, p);
+
+               /*
+                * Is someone visiting anywhere in the subtree ?
+                * If there's no mount we need to check the usage
+                * count for the autofs dentry.
+                * If the fs is busy update the expiry counter.
+                */
+               if (d_mountpoint(p)) {
+                       if (autofs_mount_busy(mnt, p)) {
+                               top_ino->last_used = jiffies;
+                               dput(p);
+                               return 1;
+                       }
+               } else {
+                       struct autofs_info *ino = autofs_dentry_ino(p);
+                       unsigned int ino_count = atomic_read(&ino->count);
+
+                       /* allow for dget above and top is already dgot */
+                       if (p == top)
+                               ino_count += 2;
+                       else
+                               ino_count++;
+
+                       if (d_count(p) > ino_count) {
+                               top_ino->last_used = jiffies;
+                               dput(p);
+                               return 1;
+                       }
+               }
+       }
+
+       /* Timeout of a tree mount is ultimately determined by its top dentry */
+       if (!autofs_can_expire(top, timeout, do_now))
+               return 1;
+
+       return 0;
+}
+
+static struct dentry *autofs_check_leaves(struct vfsmount *mnt,
+                                         struct dentry *parent,
+                                         unsigned long timeout,
+                                         int do_now)
+{
+       struct dentry *p;
+
+       pr_debug("parent %p %pd\n", parent, parent);
+
+       p = NULL;
+       while ((p = get_next_positive_dentry(p, parent))) {
+               pr_debug("dentry %p %pd\n", p, p);
+
+               if (d_mountpoint(p)) {
+                       /* Can we umount this guy */
+                       if (autofs_mount_busy(mnt, p))
+                               continue;
+
+                       /* Can we expire this guy */
+                       if (autofs_can_expire(p, timeout, do_now))
+                               return p;
+               }
+       }
+       return NULL;
+}
+
+/* Check if we can expire a direct mount (possibly a tree) */
+struct dentry *autofs_expire_direct(struct super_block *sb,
+                                   struct vfsmount *mnt,
+                                   struct autofs_sb_info *sbi,
+                                   int how)
+{
+       unsigned long timeout;
+       struct dentry *root = dget(sb->s_root);
+       int do_now = how & AUTOFS_EXP_IMMEDIATE;
+       struct autofs_info *ino;
+
+       if (!root)
+               return NULL;
+
+       now = jiffies;
+       timeout = sbi->exp_timeout;
+
+       if (!autofs_direct_busy(mnt, root, timeout, do_now)) {
+               spin_lock(&sbi->fs_lock);
+               ino = autofs_dentry_ino(root);
+               /* No point expiring a pending mount */
+               if (ino->flags & AUTOFS_INF_PENDING) {
+                       spin_unlock(&sbi->fs_lock);
+                       goto out;
+               }
+               ino->flags |= AUTOFS_INF_WANT_EXPIRE;
+               spin_unlock(&sbi->fs_lock);
+               synchronize_rcu();
+               if (!autofs_direct_busy(mnt, root, timeout, do_now)) {
+                       spin_lock(&sbi->fs_lock);
+                       ino->flags |= AUTOFS_INF_EXPIRING;
+                       init_completion(&ino->expire_complete);
+                       spin_unlock(&sbi->fs_lock);
+                       return root;
+               }
+               spin_lock(&sbi->fs_lock);
+               ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
+               spin_unlock(&sbi->fs_lock);
+       }
+out:
+       dput(root);
+
+       return NULL;
+}
+
+/* Check if 'dentry' should expire, or return a nearby
+ * dentry that is suitable.
+ * If returned dentry is different from arg dentry,
+ * then a dget() reference was taken, else not.
+ */
+static struct dentry *should_expire(struct dentry *dentry,
+                                   struct vfsmount *mnt,
+                                   unsigned long timeout,
+                                   int how)
+{
+       int do_now = how & AUTOFS_EXP_IMMEDIATE;
+       int exp_leaves = how & AUTOFS_EXP_LEAVES;
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+       unsigned int ino_count;
+
+       /* No point expiring a pending mount */
+       if (ino->flags & AUTOFS_INF_PENDING)
+               return NULL;
+
+       /*
+        * Case 1: (i) indirect mount or top level pseudo direct mount
+        *         (autofs-4.1).
+        *         (ii) indirect mount with offset mount, check the "/"
+        *         offset (autofs-5.0+).
+        */
+       if (d_mountpoint(dentry)) {
+               pr_debug("checking mountpoint %p %pd\n", dentry, dentry);
+
+               /* Can we umount this guy */
+               if (autofs_mount_busy(mnt, dentry))
+                       return NULL;
+
+               /* Can we expire this guy */
+               if (autofs_can_expire(dentry, timeout, do_now))
+                       return dentry;
+               return NULL;
+       }
+
+       if (d_really_is_positive(dentry) && d_is_symlink(dentry)) {
+               pr_debug("checking symlink %p %pd\n", dentry, dentry);
+               /*
+                * A symlink can't be "busy" in the usual sense so
+                * just check last used for expire timeout.
+                */
+               if (autofs_can_expire(dentry, timeout, do_now))
+                       return dentry;
+               return NULL;
+       }
+
+       if (simple_empty(dentry))
+               return NULL;
+
+       /* Case 2: tree mount, expire iff entire tree is not busy */
+       if (!exp_leaves) {
+               /* Path walk currently on this dentry? */
+               ino_count = atomic_read(&ino->count) + 1;
+               if (d_count(dentry) > ino_count)
+                       return NULL;
+
+               if (!autofs_tree_busy(mnt, dentry, timeout, do_now))
+                       return dentry;
+       /*
+        * Case 3: pseudo direct mount, expire individual leaves
+        *         (autofs-4.1).
+        */
+       } else {
+               /* Path walk currently on this dentry? */
+               struct dentry *expired;
+
+               ino_count = atomic_read(&ino->count) + 1;
+               if (d_count(dentry) > ino_count)
+                       return NULL;
+
+               expired = autofs_check_leaves(mnt, dentry, timeout, do_now);
+               if (expired) {
+                       if (expired == dentry)
+                               dput(dentry);
+                       return expired;
+               }
+       }
+       return NULL;
+}
+
+/*
+ * Find an eligible tree to time-out
+ * A tree is eligible if :-
+ *  - it is unused by any user process
+ *  - it has been unused for exp_timeout time
+ */
+struct dentry *autofs_expire_indirect(struct super_block *sb,
+                                     struct vfsmount *mnt,
+                                     struct autofs_sb_info *sbi,
+                                     int how)
+{
+       unsigned long timeout;
+       struct dentry *root = sb->s_root;
+       struct dentry *dentry;
+       struct dentry *expired;
+       struct dentry *found;
+       struct autofs_info *ino;
+
+       if (!root)
+               return NULL;
+
+       now = jiffies;
+       timeout = sbi->exp_timeout;
+
+       dentry = NULL;
+       while ((dentry = get_next_positive_subdir(dentry, root))) {
+               int flags = how;
+
+               spin_lock(&sbi->fs_lock);
+               ino = autofs_dentry_ino(dentry);
+               if (ino->flags & AUTOFS_INF_WANT_EXPIRE) {
+                       spin_unlock(&sbi->fs_lock);
+                       continue;
+               }
+               spin_unlock(&sbi->fs_lock);
+
+               expired = should_expire(dentry, mnt, timeout, flags);
+               if (!expired)
+                       continue;
+
+               spin_lock(&sbi->fs_lock);
+               ino = autofs_dentry_ino(expired);
+               ino->flags |= AUTOFS_INF_WANT_EXPIRE;
+               spin_unlock(&sbi->fs_lock);
+               synchronize_rcu();
+
+               /* Make sure a reference is not taken on found if
+                * things have changed.
+                */
+               flags &= ~AUTOFS_EXP_LEAVES;
+               found = should_expire(expired, mnt, timeout, how);
+               if (!found || found != expired)
+                       /* Something has changed, continue */
+                       goto next;
+
+               if (expired != dentry)
+                       dput(dentry);
+
+               spin_lock(&sbi->fs_lock);
+               goto found;
+next:
+               spin_lock(&sbi->fs_lock);
+               ino->flags &= ~AUTOFS_INF_WANT_EXPIRE;
+               spin_unlock(&sbi->fs_lock);
+               if (expired != dentry)
+                       dput(expired);
+       }
+       return NULL;
+
+found:
+       pr_debug("returning %p %pd\n", expired, expired);
+       ino->flags |= AUTOFS_INF_EXPIRING;
+       init_completion(&ino->expire_complete);
+       spin_unlock(&sbi->fs_lock);
+       return expired;
+}
+
+int autofs_expire_wait(const struct path *path, int rcu_walk)
+{
+       struct dentry *dentry = path->dentry;
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+       int status;
+       int state;
+
+       /* Block on any pending expire */
+       if (!(ino->flags & AUTOFS_INF_WANT_EXPIRE))
+               return 0;
+       if (rcu_walk)
+               return -ECHILD;
+
+retry:
+       spin_lock(&sbi->fs_lock);
+       state = ino->flags & (AUTOFS_INF_WANT_EXPIRE | AUTOFS_INF_EXPIRING);
+       if (state == AUTOFS_INF_WANT_EXPIRE) {
+               spin_unlock(&sbi->fs_lock);
+               /*
+                * Possibly being selected for expire, wait until
+                * it's selected or not.
+                */
+               schedule_timeout_uninterruptible(HZ/10);
+               goto retry;
+       }
+       if (state & AUTOFS_INF_EXPIRING) {
+               spin_unlock(&sbi->fs_lock);
+
+               pr_debug("waiting for expire %p name=%pd\n", dentry, dentry);
+
+               status = autofs_wait(sbi, path, NFY_NONE);
+               wait_for_completion(&ino->expire_complete);
+
+               pr_debug("expire done status=%d\n", status);
+
+               if (d_unhashed(dentry))
+                       return -EAGAIN;
+
+               return status;
+       }
+       spin_unlock(&sbi->fs_lock);
+
+       return 0;
+}
+
+/* Perform an expiry operation */
+int autofs_expire_run(struct super_block *sb,
+                     struct vfsmount *mnt,
+                     struct autofs_sb_info *sbi,
+                     struct autofs_packet_expire __user *pkt_p)
+{
+       struct autofs_packet_expire pkt;
+       struct autofs_info *ino;
+       struct dentry *dentry;
+       int ret = 0;
+
+       memset(&pkt, 0, sizeof(pkt));
+
+       pkt.hdr.proto_version = sbi->version;
+       pkt.hdr.type = autofs_ptype_expire;
+
+       dentry = autofs_expire_indirect(sb, mnt, sbi, 0);
+       if (!dentry)
+               return -EAGAIN;
+
+       pkt.len = dentry->d_name.len;
+       memcpy(pkt.name, dentry->d_name.name, pkt.len);
+       pkt.name[pkt.len] = '\0';
+       dput(dentry);
+
+       if (copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)))
+               ret = -EFAULT;
+
+       spin_lock(&sbi->fs_lock);
+       ino = autofs_dentry_ino(dentry);
+       /* avoid rapid-fire expire attempts if expiry fails */
+       ino->last_used = now;
+       ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
+       complete_all(&ino->expire_complete);
+       spin_unlock(&sbi->fs_lock);
+
+       return ret;
+}
+
+int autofs_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
+                          struct autofs_sb_info *sbi, int when)
+{
+       struct dentry *dentry;
+       int ret = -EAGAIN;
+
+       if (autofs_type_trigger(sbi->type))
+               dentry = autofs_expire_direct(sb, mnt, sbi, when);
+       else
+               dentry = autofs_expire_indirect(sb, mnt, sbi, when);
+
+       if (dentry) {
+               struct autofs_info *ino = autofs_dentry_ino(dentry);
+               const struct path path = { .mnt = mnt, .dentry = dentry };
+
+               /* This is synchronous because it makes the daemon a
+                * little easier
+                */
+               ret = autofs_wait(sbi, &path, NFY_EXPIRE);
+
+               spin_lock(&sbi->fs_lock);
+               /* avoid rapid-fire expire attempts if expiry fails */
+               ino->last_used = now;
+               ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
+               complete_all(&ino->expire_complete);
+               spin_unlock(&sbi->fs_lock);
+               dput(dentry);
+       }
+
+       return ret;
+}
+
+/*
+ * Call repeatedly until it returns -EAGAIN, meaning there's nothing
+ * more to be done.
+ */
+int autofs_expire_multi(struct super_block *sb, struct vfsmount *mnt,
+                       struct autofs_sb_info *sbi, int __user *arg)
+{
+       int do_now = 0;
+
+       if (arg && get_user(do_now, arg))
+               return -EFAULT;
+
+       return autofs_do_expire_multi(sb, mnt, sbi, do_now);
+}
diff --git a/fs/autofs/init.c b/fs/autofs/init.c
new file mode 100644 (file)
index 0000000..16fb613
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include "autofs_i.h"
+
+static struct dentry *autofs_mount(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *data)
+{
+       return mount_nodev(fs_type, flags, data, autofs_fill_super);
+}
+
+static struct file_system_type autofs_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "autofs",
+       .mount          = autofs_mount,
+       .kill_sb        = autofs_kill_sb,
+};
+MODULE_ALIAS_FS("autofs");
+
+static int __init init_autofs_fs(void)
+{
+       int err;
+
+       autofs_dev_ioctl_init();
+
+       err = register_filesystem(&autofs_fs_type);
+       if (err)
+               autofs_dev_ioctl_exit();
+
+       return err;
+}
+
+static void __exit exit_autofs_fs(void)
+{
+       autofs_dev_ioctl_exit();
+       unregister_filesystem(&autofs_fs_type);
+}
+
+module_init(init_autofs_fs)
+module_exit(exit_autofs_fs)
+MODULE_LICENSE("GPL");
diff --git a/fs/autofs/inode.c b/fs/autofs/inode.c
new file mode 100644 (file)
index 0000000..b51980f
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ * Copyright 2005-2006 Ian Kent <raven@themaw.net>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+#include <linux/seq_file.h>
+#include <linux/pagemap.h>
+#include <linux/parser.h>
+#include <linux/magic.h>
+
+#include "autofs_i.h"
+
+struct autofs_info *autofs_new_ino(struct autofs_sb_info *sbi)
+{
+       struct autofs_info *ino;
+
+       ino = kzalloc(sizeof(*ino), GFP_KERNEL);
+       if (ino) {
+               INIT_LIST_HEAD(&ino->active);
+               INIT_LIST_HEAD(&ino->expiring);
+               ino->last_used = jiffies;
+               ino->sbi = sbi;
+       }
+       return ino;
+}
+
+void autofs_clean_ino(struct autofs_info *ino)
+{
+       ino->uid = GLOBAL_ROOT_UID;
+       ino->gid = GLOBAL_ROOT_GID;
+       ino->last_used = jiffies;
+}
+
+void autofs_free_ino(struct autofs_info *ino)
+{
+       kfree(ino);
+}
+
+void autofs_kill_sb(struct super_block *sb)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(sb);
+
+       /*
+        * In the event of a failure in get_sb_nodev the superblock
+        * info is not present so nothing else has been setup, so
+        * just call kill_anon_super when we are called from
+        * deactivate_super.
+        */
+       if (sbi) {
+               /* Free wait queues, close pipe */
+               autofs_catatonic_mode(sbi);
+               put_pid(sbi->oz_pgrp);
+       }
+
+       pr_debug("shutting down\n");
+       kill_litter_super(sb);
+       if (sbi)
+               kfree_rcu(sbi, rcu);
+}
+
+static int autofs_show_options(struct seq_file *m, struct dentry *root)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(root->d_sb);
+       struct inode *root_inode = d_inode(root->d_sb->s_root);
+
+       if (!sbi)
+               return 0;
+
+       seq_printf(m, ",fd=%d", sbi->pipefd);
+       if (!uid_eq(root_inode->i_uid, GLOBAL_ROOT_UID))
+               seq_printf(m, ",uid=%u",
+                       from_kuid_munged(&init_user_ns, root_inode->i_uid));
+       if (!gid_eq(root_inode->i_gid, GLOBAL_ROOT_GID))
+               seq_printf(m, ",gid=%u",
+                       from_kgid_munged(&init_user_ns, root_inode->i_gid));
+       seq_printf(m, ",pgrp=%d", pid_vnr(sbi->oz_pgrp));
+       seq_printf(m, ",timeout=%lu", sbi->exp_timeout/HZ);
+       seq_printf(m, ",minproto=%d", sbi->min_proto);
+       seq_printf(m, ",maxproto=%d", sbi->max_proto);
+
+       if (autofs_type_offset(sbi->type))
+               seq_printf(m, ",offset");
+       else if (autofs_type_direct(sbi->type))
+               seq_printf(m, ",direct");
+       else
+               seq_printf(m, ",indirect");
+#ifdef CONFIG_CHECKPOINT_RESTORE
+       if (sbi->pipe)
+               seq_printf(m, ",pipe_ino=%ld", file_inode(sbi->pipe)->i_ino);
+       else
+               seq_printf(m, ",pipe_ino=-1");
+#endif
+       return 0;
+}
+
+static void autofs_evict_inode(struct inode *inode)
+{
+       clear_inode(inode);
+       kfree(inode->i_private);
+}
+
+static const struct super_operations autofs_sops = {
+       .statfs         = simple_statfs,
+       .show_options   = autofs_show_options,
+       .evict_inode    = autofs_evict_inode,
+};
+
+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto,
+       Opt_indirect, Opt_direct, Opt_offset};
+
+static const match_table_t tokens = {
+       {Opt_fd, "fd=%u"},
+       {Opt_uid, "uid=%u"},
+       {Opt_gid, "gid=%u"},
+       {Opt_pgrp, "pgrp=%u"},
+       {Opt_minproto, "minproto=%u"},
+       {Opt_maxproto, "maxproto=%u"},
+       {Opt_indirect, "indirect"},
+       {Opt_direct, "direct"},
+       {Opt_offset, "offset"},
+       {Opt_err, NULL}
+};
+
+static int parse_options(char *options, int *pipefd, kuid_t *uid, kgid_t *gid,
+                        int *pgrp, bool *pgrp_set, unsigned int *type,
+                        int *minproto, int *maxproto)
+{
+       char *p;
+       substring_t args[MAX_OPT_ARGS];
+       int option;
+
+       *uid = current_uid();
+       *gid = current_gid();
+
+       *minproto = AUTOFS_MIN_PROTO_VERSION;
+       *maxproto = AUTOFS_MAX_PROTO_VERSION;
+
+       *pipefd = -1;
+
+       if (!options)
+               return 1;
+
+       while ((p = strsep(&options, ",")) != NULL) {
+               int token;
+
+               if (!*p)
+                       continue;
+
+               token = match_token(p, tokens, args);
+               switch (token) {
+               case Opt_fd:
+                       if (match_int(args, pipefd))
+                               return 1;
+                       break;
+               case Opt_uid:
+                       if (match_int(args, &option))
+                               return 1;
+                       *uid = make_kuid(current_user_ns(), option);
+                       if (!uid_valid(*uid))
+                               return 1;
+                       break;
+               case Opt_gid:
+                       if (match_int(args, &option))
+                               return 1;
+                       *gid = make_kgid(current_user_ns(), option);
+                       if (!gid_valid(*gid))
+                               return 1;
+                       break;
+               case Opt_pgrp:
+                       if (match_int(args, &option))
+                               return 1;
+                       *pgrp = option;
+                       *pgrp_set = true;
+                       break;
+               case Opt_minproto:
+                       if (match_int(args, &option))
+                               return 1;
+                       *minproto = option;
+                       break;
+               case Opt_maxproto:
+                       if (match_int(args, &option))
+                               return 1;
+                       *maxproto = option;
+                       break;
+               case Opt_indirect:
+                       set_autofs_type_indirect(type);
+                       break;
+               case Opt_direct:
+                       set_autofs_type_direct(type);
+                       break;
+               case Opt_offset:
+                       set_autofs_type_offset(type);
+                       break;
+               default:
+                       return 1;
+               }
+       }
+       return (*pipefd < 0);
+}
+
+int autofs_fill_super(struct super_block *s, void *data, int silent)
+{
+       struct inode *root_inode;
+       struct dentry *root;
+       struct file *pipe;
+       int pipefd;
+       struct autofs_sb_info *sbi;
+       struct autofs_info *ino;
+       int pgrp = 0;
+       bool pgrp_set = false;
+       int ret = -EINVAL;
+
+       sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
+       if (!sbi)
+               return -ENOMEM;
+       pr_debug("starting up, sbi = %p\n", sbi);
+
+       s->s_fs_info = sbi;
+       sbi->magic = AUTOFS_SBI_MAGIC;
+       sbi->pipefd = -1;
+       sbi->pipe = NULL;
+       sbi->catatonic = 1;
+       sbi->exp_timeout = 0;
+       sbi->oz_pgrp = NULL;
+       sbi->sb = s;
+       sbi->version = 0;
+       sbi->sub_version = 0;
+       set_autofs_type_indirect(&sbi->type);
+       sbi->min_proto = 0;
+       sbi->max_proto = 0;
+       mutex_init(&sbi->wq_mutex);
+       mutex_init(&sbi->pipe_mutex);
+       spin_lock_init(&sbi->fs_lock);
+       sbi->queues = NULL;
+       spin_lock_init(&sbi->lookup_lock);
+       INIT_LIST_HEAD(&sbi->active_list);
+       INIT_LIST_HEAD(&sbi->expiring_list);
+       s->s_blocksize = 1024;
+       s->s_blocksize_bits = 10;
+       s->s_magic = AUTOFS_SUPER_MAGIC;
+       s->s_op = &autofs_sops;
+       s->s_d_op = &autofs_dentry_operations;
+       s->s_time_gran = 1;
+
+       /*
+        * Get the root inode and dentry, but defer checking for errors.
+        */
+       ino = autofs_new_ino(sbi);
+       if (!ino) {
+               ret = -ENOMEM;
+               goto fail_free;
+       }
+       root_inode = autofs_get_inode(s, S_IFDIR | 0755);
+       root = d_make_root(root_inode);
+       if (!root)
+               goto fail_ino;
+       pipe = NULL;
+
+       root->d_fsdata = ino;
+
+       /* Can this call block? */
+       if (parse_options(data, &pipefd, &root_inode->i_uid, &root_inode->i_gid,
+                         &pgrp, &pgrp_set, &sbi->type, &sbi->min_proto,
+                         &sbi->max_proto)) {
+               pr_err("called with bogus options\n");
+               goto fail_dput;
+       }
+
+       /* Test versions first */
+       if (sbi->max_proto < AUTOFS_MIN_PROTO_VERSION ||
+           sbi->min_proto > AUTOFS_MAX_PROTO_VERSION) {
+               pr_err("kernel does not match daemon version "
+                      "daemon (%d, %d) kernel (%d, %d)\n",
+                      sbi->min_proto, sbi->max_proto,
+                      AUTOFS_MIN_PROTO_VERSION, AUTOFS_MAX_PROTO_VERSION);
+               goto fail_dput;
+       }
+
+       /* Establish highest kernel protocol version */
+       if (sbi->max_proto > AUTOFS_MAX_PROTO_VERSION)
+               sbi->version = AUTOFS_MAX_PROTO_VERSION;
+       else
+               sbi->version = sbi->max_proto;
+       sbi->sub_version = AUTOFS_PROTO_SUBVERSION;
+
+       if (pgrp_set) {
+               sbi->oz_pgrp = find_get_pid(pgrp);
+               if (!sbi->oz_pgrp) {
+                       pr_err("could not find process group %d\n",
+                               pgrp);
+                       goto fail_dput;
+               }
+       } else {
+               sbi->oz_pgrp = get_task_pid(current, PIDTYPE_PGID);
+       }
+
+       if (autofs_type_trigger(sbi->type))
+               __managed_dentry_set_managed(root);
+
+       root_inode->i_fop = &autofs_root_operations;
+       root_inode->i_op = &autofs_dir_inode_operations;
+
+       pr_debug("pipe fd = %d, pgrp = %u\n", pipefd, pid_nr(sbi->oz_pgrp));
+       pipe = fget(pipefd);
+
+       if (!pipe) {
+               pr_err("could not open pipe file descriptor\n");
+               goto fail_put_pid;
+       }
+       ret = autofs_prepare_pipe(pipe);
+       if (ret < 0)
+               goto fail_fput;
+       sbi->pipe = pipe;
+       sbi->pipefd = pipefd;
+       sbi->catatonic = 0;
+
+       /*
+        * Success! Install the root dentry now to indicate completion.
+        */
+       s->s_root = root;
+       return 0;
+
+       /*
+        * Failure ... clean up.
+        */
+fail_fput:
+       pr_err("pipe file descriptor does not contain proper ops\n");
+       fput(pipe);
+fail_put_pid:
+       put_pid(sbi->oz_pgrp);
+fail_dput:
+       dput(root);
+       goto fail_free;
+fail_ino:
+       autofs_free_ino(ino);
+fail_free:
+       kfree(sbi);
+       s->s_fs_info = NULL;
+       return ret;
+}
+
+struct inode *autofs_get_inode(struct super_block *sb, umode_t mode)
+{
+       struct inode *inode = new_inode(sb);
+
+       if (inode == NULL)
+               return NULL;
+
+       inode->i_mode = mode;
+       if (sb->s_root) {
+               inode->i_uid = d_inode(sb->s_root)->i_uid;
+               inode->i_gid = d_inode(sb->s_root)->i_gid;
+       }
+       inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
+       inode->i_ino = get_next_ino();
+
+       if (S_ISDIR(mode)) {
+               set_nlink(inode, 2);
+               inode->i_op = &autofs_dir_inode_operations;
+               inode->i_fop = &autofs_dir_operations;
+       } else if (S_ISLNK(mode)) {
+               inode->i_op = &autofs_symlink_inode_operations;
+       } else
+               WARN_ON(1);
+
+       return inode;
+}
diff --git a/fs/autofs/root.c b/fs/autofs/root.c
new file mode 100644 (file)
index 0000000..a3d4141
--- /dev/null
@@ -0,0 +1,936 @@
+/*
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
+ * Copyright 2001-2006 Ian Kent <raven@themaw.net>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+#include <linux/capability.h>
+#include <linux/compat.h>
+
+#include "autofs_i.h"
+
+static int autofs_dir_symlink(struct inode *, struct dentry *, const char *);
+static int autofs_dir_unlink(struct inode *, struct dentry *);
+static int autofs_dir_rmdir(struct inode *, struct dentry *);
+static int autofs_dir_mkdir(struct inode *, struct dentry *, umode_t);
+static long autofs_root_ioctl(struct file *, unsigned int, unsigned long);
+#ifdef CONFIG_COMPAT
+static long autofs_root_compat_ioctl(struct file *,
+                                    unsigned int, unsigned long);
+#endif
+static int autofs_dir_open(struct inode *inode, struct file *file);
+static struct dentry *autofs_lookup(struct inode *,
+                                   struct dentry *, unsigned int);
+static struct vfsmount *autofs_d_automount(struct path *);
+static int autofs_d_manage(const struct path *, bool);
+static void autofs_dentry_release(struct dentry *);
+
+const struct file_operations autofs_root_operations = {
+       .open           = dcache_dir_open,
+       .release        = dcache_dir_close,
+       .read           = generic_read_dir,
+       .iterate_shared = dcache_readdir,
+       .llseek         = dcache_dir_lseek,
+       .unlocked_ioctl = autofs_root_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl   = autofs_root_compat_ioctl,
+#endif
+};
+
+const struct file_operations autofs_dir_operations = {
+       .open           = autofs_dir_open,
+       .release        = dcache_dir_close,
+       .read           = generic_read_dir,
+       .iterate_shared = dcache_readdir,
+       .llseek         = dcache_dir_lseek,
+};
+
+const struct inode_operations autofs_dir_inode_operations = {
+       .lookup         = autofs_lookup,
+       .unlink         = autofs_dir_unlink,
+       .symlink        = autofs_dir_symlink,
+       .mkdir          = autofs_dir_mkdir,
+       .rmdir          = autofs_dir_rmdir,
+};
+
+const struct dentry_operations autofs_dentry_operations = {
+       .d_automount    = autofs_d_automount,
+       .d_manage       = autofs_d_manage,
+       .d_release      = autofs_dentry_release,
+};
+
+static void autofs_add_active(struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct autofs_info *ino;
+
+       ino = autofs_dentry_ino(dentry);
+       if (ino) {
+               spin_lock(&sbi->lookup_lock);
+               if (!ino->active_count) {
+                       if (list_empty(&ino->active))
+                               list_add(&ino->active, &sbi->active_list);
+               }
+               ino->active_count++;
+               spin_unlock(&sbi->lookup_lock);
+       }
+}
+
+static void autofs_del_active(struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct autofs_info *ino;
+
+       ino = autofs_dentry_ino(dentry);
+       if (ino) {
+               spin_lock(&sbi->lookup_lock);
+               ino->active_count--;
+               if (!ino->active_count) {
+                       if (!list_empty(&ino->active))
+                               list_del_init(&ino->active);
+               }
+               spin_unlock(&sbi->lookup_lock);
+       }
+}
+
+static int autofs_dir_open(struct inode *inode, struct file *file)
+{
+       struct dentry *dentry = file->f_path.dentry;
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+
+       pr_debug("file=%p dentry=%p %pd\n", file, dentry, dentry);
+
+       if (autofs_oz_mode(sbi))
+               goto out;
+
+       /*
+        * An empty directory in an autofs file system is always a
+        * mount point. The daemon must have failed to mount this
+        * during lookup so it doesn't exist. This can happen, for
+        * example, if user space returns an incorrect status for a
+        * mount request. Otherwise we're doing a readdir on the
+        * autofs file system so just let the libfs routines handle
+        * it.
+        */
+       spin_lock(&sbi->lookup_lock);
+       if (!path_is_mountpoint(&file->f_path) && simple_empty(dentry)) {
+               spin_unlock(&sbi->lookup_lock);
+               return -ENOENT;
+       }
+       spin_unlock(&sbi->lookup_lock);
+
+out:
+       return dcache_dir_open(inode, file);
+}
+
+static void autofs_dentry_release(struct dentry *de)
+{
+       struct autofs_info *ino = autofs_dentry_ino(de);
+       struct autofs_sb_info *sbi = autofs_sbi(de->d_sb);
+
+       pr_debug("releasing %p\n", de);
+
+       if (!ino)
+               return;
+
+       if (sbi) {
+               spin_lock(&sbi->lookup_lock);
+               if (!list_empty(&ino->active))
+                       list_del(&ino->active);
+               if (!list_empty(&ino->expiring))
+                       list_del(&ino->expiring);
+               spin_unlock(&sbi->lookup_lock);
+       }
+
+       autofs_free_ino(ino);
+}
+
+static struct dentry *autofs_lookup_active(struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct dentry *parent = dentry->d_parent;
+       const struct qstr *name = &dentry->d_name;
+       unsigned int len = name->len;
+       unsigned int hash = name->hash;
+       const unsigned char *str = name->name;
+       struct list_head *p, *head;
+
+       head = &sbi->active_list;
+       if (list_empty(head))
+               return NULL;
+       spin_lock(&sbi->lookup_lock);
+       list_for_each(p, head) {
+               struct autofs_info *ino;
+               struct dentry *active;
+               const struct qstr *qstr;
+
+               ino = list_entry(p, struct autofs_info, active);
+               active = ino->dentry;
+
+               spin_lock(&active->d_lock);
+
+               /* Already gone? */
+               if ((int) d_count(active) <= 0)
+                       goto next;
+
+               qstr = &active->d_name;
+
+               if (active->d_name.hash != hash)
+                       goto next;
+               if (active->d_parent != parent)
+                       goto next;
+
+               if (qstr->len != len)
+                       goto next;
+               if (memcmp(qstr->name, str, len))
+                       goto next;
+
+               if (d_unhashed(active)) {
+                       dget_dlock(active);
+                       spin_unlock(&active->d_lock);
+                       spin_unlock(&sbi->lookup_lock);
+                       return active;
+               }
+next:
+               spin_unlock(&active->d_lock);
+       }
+       spin_unlock(&sbi->lookup_lock);
+
+       return NULL;
+}
+
+static struct dentry *autofs_lookup_expiring(struct dentry *dentry,
+                                            bool rcu_walk)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct dentry *parent = dentry->d_parent;
+       const struct qstr *name = &dentry->d_name;
+       unsigned int len = name->len;
+       unsigned int hash = name->hash;
+       const unsigned char *str = name->name;
+       struct list_head *p, *head;
+
+       head = &sbi->expiring_list;
+       if (list_empty(head))
+               return NULL;
+       spin_lock(&sbi->lookup_lock);
+       list_for_each(p, head) {
+               struct autofs_info *ino;
+               struct dentry *expiring;
+               const struct qstr *qstr;
+
+               if (rcu_walk) {
+                       spin_unlock(&sbi->lookup_lock);
+                       return ERR_PTR(-ECHILD);
+               }
+
+               ino = list_entry(p, struct autofs_info, expiring);
+               expiring = ino->dentry;
+
+               spin_lock(&expiring->d_lock);
+
+               /* We've already been dentry_iput or unlinked */
+               if (d_really_is_negative(expiring))
+                       goto next;
+
+               qstr = &expiring->d_name;
+
+               if (expiring->d_name.hash != hash)
+                       goto next;
+               if (expiring->d_parent != parent)
+                       goto next;
+
+               if (qstr->len != len)
+                       goto next;
+               if (memcmp(qstr->name, str, len))
+                       goto next;
+
+               if (d_unhashed(expiring)) {
+                       dget_dlock(expiring);
+                       spin_unlock(&expiring->d_lock);
+                       spin_unlock(&sbi->lookup_lock);
+                       return expiring;
+               }
+next:
+               spin_unlock(&expiring->d_lock);
+       }
+       spin_unlock(&sbi->lookup_lock);
+
+       return NULL;
+}
+
+static int autofs_mount_wait(const struct path *path, bool rcu_walk)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(path->dentry->d_sb);
+       struct autofs_info *ino = autofs_dentry_ino(path->dentry);
+       int status = 0;
+
+       if (ino->flags & AUTOFS_INF_PENDING) {
+               if (rcu_walk)
+                       return -ECHILD;
+               pr_debug("waiting for mount name=%pd\n", path->dentry);
+               status = autofs_wait(sbi, path, NFY_MOUNT);
+               pr_debug("mount wait done status=%d\n", status);
+       }
+       ino->last_used = jiffies;
+       return status;
+}
+
+static int do_expire_wait(const struct path *path, bool rcu_walk)
+{
+       struct dentry *dentry = path->dentry;
+       struct dentry *expiring;
+
+       expiring = autofs_lookup_expiring(dentry, rcu_walk);
+       if (IS_ERR(expiring))
+               return PTR_ERR(expiring);
+       if (!expiring)
+               return autofs_expire_wait(path, rcu_walk);
+       else {
+               const struct path this = { .mnt = path->mnt, .dentry = expiring };
+               /*
+                * If we are racing with expire the request might not
+                * be quite complete, but the directory has been removed
+                * so it must have been successful, just wait for it.
+                */
+               autofs_expire_wait(&this, 0);
+               autofs_del_expiring(expiring);
+               dput(expiring);
+       }
+       return 0;
+}
+
+static struct dentry *autofs_mountpoint_changed(struct path *path)
+{
+       struct dentry *dentry = path->dentry;
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+
+       /*
+        * If this is an indirect mount the dentry could have gone away
+        * as a result of an expire and a new one created.
+        */
+       if (autofs_type_indirect(sbi->type) && d_unhashed(dentry)) {
+               struct dentry *parent = dentry->d_parent;
+               struct autofs_info *ino;
+               struct dentry *new;
+
+               new = d_lookup(parent, &dentry->d_name);
+               if (!new)
+                       return NULL;
+               ino = autofs_dentry_ino(new);
+               ino->last_used = jiffies;
+               dput(path->dentry);
+               path->dentry = new;
+       }
+       return path->dentry;
+}
+
+static struct vfsmount *autofs_d_automount(struct path *path)
+{
+       struct dentry *dentry = path->dentry;
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+       int status;
+
+       pr_debug("dentry=%p %pd\n", dentry, dentry);
+
+       /* The daemon never triggers a mount. */
+       if (autofs_oz_mode(sbi))
+               return NULL;
+
+       /*
+        * If an expire request is pending everyone must wait.
+        * If the expire fails we're still mounted so continue
+        * the follow and return. A return of -EAGAIN (which only
+        * happens with indirect mounts) means the expire completed
+        * and the directory was removed, so just go ahead and try
+        * the mount.
+        */
+       status = do_expire_wait(path, 0);
+       if (status && status != -EAGAIN)
+               return NULL;
+
+       /* Callback to the daemon to perform the mount or wait */
+       spin_lock(&sbi->fs_lock);
+       if (ino->flags & AUTOFS_INF_PENDING) {
+               spin_unlock(&sbi->fs_lock);
+               status = autofs_mount_wait(path, 0);
+               if (status)
+                       return ERR_PTR(status);
+               goto done;
+       }
+
+       /*
+        * If the dentry is a symlink it's equivalent to a directory
+        * having path_is_mountpoint() true, so there's no need to call
+        * back to the daemon.
+        */
+       if (d_really_is_positive(dentry) && d_is_symlink(dentry)) {
+               spin_unlock(&sbi->fs_lock);
+               goto done;
+       }
+
+       if (!path_is_mountpoint(path)) {
+               /*
+                * It's possible that user space hasn't removed directories
+                * after umounting a rootless multi-mount, although it
+                * should. For v5 path_has_submounts() is sufficient to
+                * handle this because the leaves of the directory tree under
+                * the mount never trigger mounts themselves (they have an
+                * autofs trigger mount mounted on them). But v4 pseudo direct
+                * mounts do need the leaves to trigger mounts. In this case
+                * we have no choice but to use the list_empty() check and
+                * require user space behave.
+                */
+               if (sbi->version > 4) {
+                       if (path_has_submounts(path)) {
+                               spin_unlock(&sbi->fs_lock);
+                               goto done;
+                       }
+               } else {
+                       if (!simple_empty(dentry)) {
+                               spin_unlock(&sbi->fs_lock);
+                               goto done;
+                       }
+               }
+               ino->flags |= AUTOFS_INF_PENDING;
+               spin_unlock(&sbi->fs_lock);
+               status = autofs_mount_wait(path, 0);
+               spin_lock(&sbi->fs_lock);
+               ino->flags &= ~AUTOFS_INF_PENDING;
+               if (status) {
+                       spin_unlock(&sbi->fs_lock);
+                       return ERR_PTR(status);
+               }
+       }
+       spin_unlock(&sbi->fs_lock);
+done:
+       /* Mount succeeded, check if we ended up with a new dentry */
+       dentry = autofs_mountpoint_changed(path);
+       if (!dentry)
+               return ERR_PTR(-ENOENT);
+
+       return NULL;
+}
+
+static int autofs_d_manage(const struct path *path, bool rcu_walk)
+{
+       struct dentry *dentry = path->dentry;
+       struct autofs_sb_info *sbi = autofs_sbi(dentry->d_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+       int status;
+
+       pr_debug("dentry=%p %pd\n", dentry, dentry);
+
+       /* The daemon never waits. */
+       if (autofs_oz_mode(sbi)) {
+               if (!path_is_mountpoint(path))
+                       return -EISDIR;
+               return 0;
+       }
+
+       /* Wait for pending expires */
+       if (do_expire_wait(path, rcu_walk) == -ECHILD)
+               return -ECHILD;
+
+       /*
+        * This dentry may be under construction so wait on mount
+        * completion.
+        */
+       status = autofs_mount_wait(path, rcu_walk);
+       if (status)
+               return status;
+
+       if (rcu_walk) {
+               /* We don't need fs_lock in rcu_walk mode,
+                * just testing 'AUTOFS_INFO_NO_RCU' is enough.
+                * simple_empty() takes a spinlock, so leave it
+                * to last.
+                * We only return -EISDIR when certain this isn't
+                * a mount-trap.
+                */
+               struct inode *inode;
+
+               if (ino->flags & AUTOFS_INF_WANT_EXPIRE)
+                       return 0;
+               if (path_is_mountpoint(path))
+                       return 0;
+               inode = d_inode_rcu(dentry);
+               if (inode && S_ISLNK(inode->i_mode))
+                       return -EISDIR;
+               if (list_empty(&dentry->d_subdirs))
+                       return 0;
+               if (!simple_empty(dentry))
+                       return -EISDIR;
+               return 0;
+       }
+
+       spin_lock(&sbi->fs_lock);
+       /*
+        * If the dentry has been selected for expire while we slept
+        * on the lock then it might go away. We'll deal with that in
+        * ->d_automount() and wait on a new mount if the expire
+        * succeeds or return here if it doesn't (since there's no
+        * mount to follow with a rootless multi-mount).
+        */
+       if (!(ino->flags & AUTOFS_INF_EXPIRING)) {
+               /*
+                * Any needed mounting has been completed and the path
+                * updated so check if this is a rootless multi-mount so
+                * we can avoid needless calls ->d_automount() and avoid
+                * an incorrect ELOOP error return.
+                */
+               if ((!path_is_mountpoint(path) && !simple_empty(dentry)) ||
+                   (d_really_is_positive(dentry) && d_is_symlink(dentry)))
+                       status = -EISDIR;
+       }
+       spin_unlock(&sbi->fs_lock);
+
+       return status;
+}
+
+/* Lookups in the root directory */
+static struct dentry *autofs_lookup(struct inode *dir,
+                                   struct dentry *dentry, unsigned int flags)
+{
+       struct autofs_sb_info *sbi;
+       struct autofs_info *ino;
+       struct dentry *active;
+
+       pr_debug("name = %pd\n", dentry);
+
+       /* File name too long to exist */
+       if (dentry->d_name.len > NAME_MAX)
+               return ERR_PTR(-ENAMETOOLONG);
+
+       sbi = autofs_sbi(dir->i_sb);
+
+       pr_debug("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n",
+                current->pid, task_pgrp_nr(current), sbi->catatonic,
+                autofs_oz_mode(sbi));
+
+       active = autofs_lookup_active(dentry);
+       if (active)
+               return active;
+       else {
+               /*
+                * A dentry that is not within the root can never trigger a
+                * mount operation, unless the directory already exists, so we
+                * can return fail immediately.  The daemon however does need
+                * to create directories within the file system.
+                */
+               if (!autofs_oz_mode(sbi) && !IS_ROOT(dentry->d_parent))
+                       return ERR_PTR(-ENOENT);
+
+               /* Mark entries in the root as mount triggers */
+               if (IS_ROOT(dentry->d_parent) &&
+                   autofs_type_indirect(sbi->type))
+                       __managed_dentry_set_managed(dentry);
+
+               ino = autofs_new_ino(sbi);
+               if (!ino)
+                       return ERR_PTR(-ENOMEM);
+
+               dentry->d_fsdata = ino;
+               ino->dentry = dentry;
+
+               autofs_add_active(dentry);
+       }
+       return NULL;
+}
+
+static int autofs_dir_symlink(struct inode *dir,
+                              struct dentry *dentry,
+                              const char *symname)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+       struct autofs_info *p_ino;
+       struct inode *inode;
+       size_t size = strlen(symname);
+       char *cp;
+
+       pr_debug("%s <- %pd\n", symname, dentry);
+
+       if (!autofs_oz_mode(sbi))
+               return -EACCES;
+
+       BUG_ON(!ino);
+
+       autofs_clean_ino(ino);
+
+       autofs_del_active(dentry);
+
+       cp = kmalloc(size + 1, GFP_KERNEL);
+       if (!cp)
+               return -ENOMEM;
+
+       strcpy(cp, symname);
+
+       inode = autofs_get_inode(dir->i_sb, S_IFLNK | 0555);
+       if (!inode) {
+               kfree(cp);
+               return -ENOMEM;
+       }
+       inode->i_private = cp;
+       inode->i_size = size;
+       d_add(dentry, inode);
+
+       dget(dentry);
+       atomic_inc(&ino->count);
+       p_ino = autofs_dentry_ino(dentry->d_parent);
+       if (p_ino && !IS_ROOT(dentry))
+               atomic_inc(&p_ino->count);
+
+       dir->i_mtime = current_time(dir);
+
+       return 0;
+}
+
+/*
+ * NOTE!
+ *
+ * Normal filesystems would do a "d_delete()" to tell the VFS dcache
+ * that the file no longer exists. However, doing that means that the
+ * VFS layer can turn the dentry into a negative dentry.  We don't want
+ * this, because the unlink is probably the result of an expire.
+ * We simply d_drop it and add it to a expiring list in the super block,
+ * which allows the dentry lookup to check for an incomplete expire.
+ *
+ * If a process is blocked on the dentry waiting for the expire to finish,
+ * it will invalidate the dentry and try to mount with a new one.
+ *
+ * Also see autofs_dir_rmdir()..
+ */
+static int autofs_dir_unlink(struct inode *dir, struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+       struct autofs_info *p_ino;
+
+       /* This allows root to remove symlinks */
+       if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (atomic_dec_and_test(&ino->count)) {
+               p_ino = autofs_dentry_ino(dentry->d_parent);
+               if (p_ino && !IS_ROOT(dentry))
+                       atomic_dec(&p_ino->count);
+       }
+       dput(ino->dentry);
+
+       d_inode(dentry)->i_size = 0;
+       clear_nlink(d_inode(dentry));
+
+       dir->i_mtime = current_time(dir);
+
+       spin_lock(&sbi->lookup_lock);
+       __autofs_add_expiring(dentry);
+       d_drop(dentry);
+       spin_unlock(&sbi->lookup_lock);
+
+       return 0;
+}
+
+/*
+ * Version 4 of autofs provides a pseudo direct mount implementation
+ * that relies on directories at the leaves of a directory tree under
+ * an indirect mount to trigger mounts. To allow for this we need to
+ * set the DMANAGED_AUTOMOUNT and DMANAGED_TRANSIT flags on the leaves
+ * of the directory tree. There is no need to clear the automount flag
+ * following a mount or restore it after an expire because these mounts
+ * are always covered. However, it is necessary to ensure that these
+ * flags are clear on non-empty directories to avoid unnecessary calls
+ * during path walks.
+ */
+static void autofs_set_leaf_automount_flags(struct dentry *dentry)
+{
+       struct dentry *parent;
+
+       /* root and dentrys in the root are already handled */
+       if (IS_ROOT(dentry->d_parent))
+               return;
+
+       managed_dentry_set_managed(dentry);
+
+       parent = dentry->d_parent;
+       /* only consider parents below dentrys in the root */
+       if (IS_ROOT(parent->d_parent))
+               return;
+       managed_dentry_clear_managed(parent);
+}
+
+static void autofs_clear_leaf_automount_flags(struct dentry *dentry)
+{
+       struct list_head *d_child;
+       struct dentry *parent;
+
+       /* flags for dentrys in the root are handled elsewhere */
+       if (IS_ROOT(dentry->d_parent))
+               return;
+
+       managed_dentry_clear_managed(dentry);
+
+       parent = dentry->d_parent;
+       /* only consider parents below dentrys in the root */
+       if (IS_ROOT(parent->d_parent))
+               return;
+       d_child = &dentry->d_child;
+       /* Set parent managed if it's becoming empty */
+       if (d_child->next == &parent->d_subdirs &&
+           d_child->prev == &parent->d_subdirs)
+               managed_dentry_set_managed(parent);
+}
+
+static int autofs_dir_rmdir(struct inode *dir, struct dentry *dentry)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+       struct autofs_info *p_ino;
+
+       pr_debug("dentry %p, removing %pd\n", dentry, dentry);
+
+       if (!autofs_oz_mode(sbi))
+               return -EACCES;
+
+       spin_lock(&sbi->lookup_lock);
+       if (!simple_empty(dentry)) {
+               spin_unlock(&sbi->lookup_lock);
+               return -ENOTEMPTY;
+       }
+       __autofs_add_expiring(dentry);
+       d_drop(dentry);
+       spin_unlock(&sbi->lookup_lock);
+
+       if (sbi->version < 5)
+               autofs_clear_leaf_automount_flags(dentry);
+
+       if (atomic_dec_and_test(&ino->count)) {
+               p_ino = autofs_dentry_ino(dentry->d_parent);
+               if (p_ino && dentry->d_parent != dentry)
+                       atomic_dec(&p_ino->count);
+       }
+       dput(ino->dentry);
+       d_inode(dentry)->i_size = 0;
+       clear_nlink(d_inode(dentry));
+
+       if (dir->i_nlink)
+               drop_nlink(dir);
+
+       return 0;
+}
+
+static int autofs_dir_mkdir(struct inode *dir,
+                           struct dentry *dentry, umode_t mode)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(dir->i_sb);
+       struct autofs_info *ino = autofs_dentry_ino(dentry);
+       struct autofs_info *p_ino;
+       struct inode *inode;
+
+       if (!autofs_oz_mode(sbi))
+               return -EACCES;
+
+       pr_debug("dentry %p, creating %pd\n", dentry, dentry);
+
+       BUG_ON(!ino);
+
+       autofs_clean_ino(ino);
+
+       autofs_del_active(dentry);
+
+       inode = autofs_get_inode(dir->i_sb, S_IFDIR | mode);
+       if (!inode)
+               return -ENOMEM;
+       d_add(dentry, inode);
+
+       if (sbi->version < 5)
+               autofs_set_leaf_automount_flags(dentry);
+
+       dget(dentry);
+       atomic_inc(&ino->count);
+       p_ino = autofs_dentry_ino(dentry->d_parent);
+       if (p_ino && !IS_ROOT(dentry))
+               atomic_inc(&p_ino->count);
+       inc_nlink(dir);
+       dir->i_mtime = current_time(dir);
+
+       return 0;
+}
+
+/* Get/set timeout ioctl() operation */
+#ifdef CONFIG_COMPAT
+static inline int autofs_compat_get_set_timeout(struct autofs_sb_info *sbi,
+                                                compat_ulong_t __user *p)
+{
+       unsigned long ntimeout;
+       int rv;
+
+       rv = get_user(ntimeout, p);
+       if (rv)
+               goto error;
+
+       rv = put_user(sbi->exp_timeout/HZ, p);
+       if (rv)
+               goto error;
+
+       if (ntimeout > UINT_MAX/HZ)
+               sbi->exp_timeout = 0;
+       else
+               sbi->exp_timeout = ntimeout * HZ;
+
+       return 0;
+error:
+       return rv;
+}
+#endif
+
+static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi,
+                                         unsigned long __user *p)
+{
+       unsigned long ntimeout;
+       int rv;
+
+       rv = get_user(ntimeout, p);
+       if (rv)
+               goto error;
+
+       rv = put_user(sbi->exp_timeout/HZ, p);
+       if (rv)
+               goto error;
+
+       if (ntimeout > ULONG_MAX/HZ)
+               sbi->exp_timeout = 0;
+       else
+               sbi->exp_timeout = ntimeout * HZ;
+
+       return 0;
+error:
+       return rv;
+}
+
+/* Return protocol version */
+static inline int autofs_get_protover(struct autofs_sb_info *sbi,
+                                      int __user *p)
+{
+       return put_user(sbi->version, p);
+}
+
+/* Return protocol sub version */
+static inline int autofs_get_protosubver(struct autofs_sb_info *sbi,
+                                         int __user *p)
+{
+       return put_user(sbi->sub_version, p);
+}
+
+/*
+* Tells the daemon whether it can umount the autofs mount.
+*/
+static inline int autofs_ask_umount(struct vfsmount *mnt, int __user *p)
+{
+       int status = 0;
+
+       if (may_umount(mnt))
+               status = 1;
+
+       pr_debug("may umount %d\n", status);
+
+       status = put_user(status, p);
+
+       return status;
+}
+
+/* Identify autofs_dentries - this is so we can tell if there's
+ * an extra dentry refcount or not.  We only hold a refcount on the
+ * dentry if its non-negative (ie, d_inode != NULL)
+ */
+int is_autofs_dentry(struct dentry *dentry)
+{
+       return dentry && d_really_is_positive(dentry) &&
+               dentry->d_op == &autofs_dentry_operations &&
+               dentry->d_fsdata != NULL;
+}
+
+/*
+ * ioctl()'s on the root directory is the chief method for the daemon to
+ * generate kernel reactions
+ */
+static int autofs_root_ioctl_unlocked(struct inode *inode, struct file *filp,
+                                      unsigned int cmd, unsigned long arg)
+{
+       struct autofs_sb_info *sbi = autofs_sbi(inode->i_sb);
+       void __user *p = (void __user *)arg;
+
+       pr_debug("cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",
+                cmd, arg, sbi, task_pgrp_nr(current));
+
+       if (_IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) ||
+            _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT)
+               return -ENOTTY;
+
+       if (!autofs_oz_mode(sbi) && !capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       switch (cmd) {
+       case AUTOFS_IOC_READY:  /* Wait queue: go ahead and retry */
+               return autofs_wait_release(sbi, (autofs_wqt_t) arg, 0);
+       case AUTOFS_IOC_FAIL:   /* Wait queue: fail with ENOENT */
+               return autofs_wait_release(sbi, (autofs_wqt_t) arg, -ENOENT);
+       case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
+               autofs_catatonic_mode(sbi);
+               return 0;
+       case AUTOFS_IOC_PROTOVER: /* Get protocol version */
+               return autofs_get_protover(sbi, p);
+       case AUTOFS_IOC_PROTOSUBVER: /* Get protocol sub version */
+               return autofs_get_protosubver(sbi, p);
+       case AUTOFS_IOC_SETTIMEOUT:
+               return autofs_get_set_timeout(sbi, p);
+#ifdef CONFIG_COMPAT
+       case AUTOFS_IOC_SETTIMEOUT32:
+               return autofs_compat_get_set_timeout(sbi, p);
+#endif
+
+       case AUTOFS_IOC_ASKUMOUNT:
+               return autofs_ask_umount(filp->f_path.mnt, p);
+
+       /* return a single thing to expire */
+       case AUTOFS_IOC_EXPIRE:
+               return autofs_expire_run(inode->i_sb, filp->f_path.mnt, sbi, p);
+       /* same as above, but can send multiple expires through pipe */
+       case AUTOFS_IOC_EXPIRE_MULTI:
+               return autofs_expire_multi(inode->i_sb,
+                                          filp->f_path.mnt, sbi, p);
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static long autofs_root_ioctl(struct file *filp,
+                              unsigned int cmd, unsigned long arg)
+{
+       struct inode *inode = file_inode(filp);
+
+       return autofs_root_ioctl_unlocked(inode, filp, cmd, arg);
+}
+
+#ifdef CONFIG_COMPAT
+static long autofs_root_compat_ioctl(struct file *filp,
+                                     unsigned int cmd, unsigned long arg)
+{
+       struct inode *inode = file_inode(filp);
+       int ret;
+
+       if (cmd == AUTOFS_IOC_READY || cmd == AUTOFS_IOC_FAIL)
+               ret = autofs_root_ioctl_unlocked(inode, filp, cmd, arg);
+       else
+               ret = autofs_root_ioctl_unlocked(inode, filp, cmd,
+                                             (unsigned long) compat_ptr(arg));
+
+       return ret;
+}
+#endif
diff --git a/fs/autofs/symlink.c b/fs/autofs/symlink.c
new file mode 100644 (file)
index 0000000..aad3902
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+#include "autofs_i.h"
+
+static const char *autofs_get_link(struct dentry *dentry,
+                                  struct inode *inode,
+                                  struct delayed_call *done)
+{
+       struct autofs_sb_info *sbi;
+       struct autofs_info *ino;
+
+       if (!dentry)
+               return ERR_PTR(-ECHILD);
+       sbi = autofs_sbi(dentry->d_sb);
+       ino = autofs_dentry_ino(dentry);
+       if (ino && !autofs_oz_mode(sbi))
+               ino->last_used = jiffies;
+       return d_inode(dentry)->i_private;
+}
+
+const struct inode_operations autofs_symlink_inode_operations = {
+       .get_link       = autofs_get_link
+};
diff --git a/fs/autofs/waitq.c b/fs/autofs/waitq.c
new file mode 100644 (file)
index 0000000..f6385c6
--- /dev/null
@@ -0,0 +1,555 @@
+/*
+ * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
+ * Copyright 2001-2006 Ian Kent <raven@themaw.net>
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ */
+
+#include <linux/sched/signal.h>
+#include "autofs_i.h"
+
+/* We make this a static variable rather than a part of the superblock; it
+ * is better if we don't reassign numbers easily even across filesystems
+ */
+static autofs_wqt_t autofs_next_wait_queue = 1;
+
+void autofs_catatonic_mode(struct autofs_sb_info *sbi)
+{
+       struct autofs_wait_queue *wq, *nwq;
+
+       mutex_lock(&sbi->wq_mutex);
+       if (sbi->catatonic) {
+               mutex_unlock(&sbi->wq_mutex);
+               return;
+       }
+
+       pr_debug("entering catatonic mode\n");
+
+       sbi->catatonic = 1;
+       wq = sbi->queues;
+       sbi->queues = NULL;     /* Erase all wait queues */
+       while (wq) {
+               nwq = wq->next;
+               wq->status = -ENOENT; /* Magic is gone - report failure */
+               kfree(wq->name.name);
+               wq->name.name = NULL;
+               wq->wait_ctr--;
+               wake_up_interruptible(&wq->queue);
+               wq = nwq;
+       }
+       fput(sbi->pipe);        /* Close the pipe */
+       sbi->pipe = NULL;
+       sbi->pipefd = -1;
+       mutex_unlock(&sbi->wq_mutex);
+}
+
+static int autofs_write(struct autofs_sb_info *sbi,
+                       struct file *file, const void *addr, int bytes)
+{
+       unsigned long sigpipe, flags;
+       const char *data = (const char *)addr;
+       ssize_t wr = 0;
+
+       sigpipe = sigismember(&current->pending.signal, SIGPIPE);
+
+       mutex_lock(&sbi->pipe_mutex);
+       while (bytes) {
+               wr = __kernel_write(file, data, bytes, &file->f_pos);
+               if (wr <= 0)
+                       break;
+               data += wr;
+               bytes -= wr;
+       }
+       mutex_unlock(&sbi->pipe_mutex);
+
+       /* Keep the currently executing process from receiving a
+        * SIGPIPE unless it was already supposed to get one
+        */
+       if (wr == -EPIPE && !sigpipe) {
+               spin_lock_irqsave(&current->sighand->siglock, flags);
+               sigdelset(&current->pending.signal, SIGPIPE);
+               recalc_sigpending();
+               spin_unlock_irqrestore(&current->sighand->siglock, flags);
+       }
+
+       /* if 'wr' returned 0 (impossible) we assume -EIO (safe) */
+       return bytes == 0 ? 0 : wr < 0 ? wr : -EIO;
+}
+
+static void autofs_notify_daemon(struct autofs_sb_info *sbi,
+                                struct autofs_wait_queue *wq,
+                                int type)
+{
+       union {
+               struct autofs_packet_hdr hdr;
+               union autofs_packet_union v4_pkt;
+               union autofs_v5_packet_union v5_pkt;
+       } pkt;
+       struct file *pipe = NULL;
+       size_t pktsz;
+       int ret;
+
+       pr_debug("wait id = 0x%08lx, name = %.*s, type=%d\n",
+                (unsigned long) wq->wait_queue_token,
+                wq->name.len, wq->name.name, type);
+
+       memset(&pkt, 0, sizeof(pkt)); /* For security reasons */
+
+       pkt.hdr.proto_version = sbi->version;
+       pkt.hdr.type = type;
+
+       switch (type) {
+       /* Kernel protocol v4 missing and expire packets */
+       case autofs_ptype_missing:
+       {
+               struct autofs_packet_missing *mp = &pkt.v4_pkt.missing;
+
+               pktsz = sizeof(*mp);
+
+               mp->wait_queue_token = wq->wait_queue_token;
+               mp->len = wq->name.len;
+               memcpy(mp->name, wq->name.name, wq->name.len);
+               mp->name[wq->name.len] = '\0';
+               break;
+       }
+       case autofs_ptype_expire_multi:
+       {
+               struct autofs_packet_expire_multi *ep =
+                                       &pkt.v4_pkt.expire_multi;
+
+               pktsz = sizeof(*ep);
+
+               ep->wait_queue_token = wq->wait_queue_token;
+               ep->len = wq->name.len;
+               memcpy(ep->name, wq->name.name, wq->name.len);
+               ep->name[wq->name.len] = '\0';
+               break;
+       }
+       /*
+        * Kernel protocol v5 packet for handling indirect and direct
+        * mount missing and expire requests
+        */
+       case autofs_ptype_missing_indirect:
+       case autofs_ptype_expire_indirect:
+       case autofs_ptype_missing_direct:
+       case autofs_ptype_expire_direct:
+       {
+               struct autofs_v5_packet *packet = &pkt.v5_pkt.v5_packet;
+               struct user_namespace *user_ns = sbi->pipe->f_cred->user_ns;
+
+               pktsz = sizeof(*packet);
+
+               packet->wait_queue_token = wq->wait_queue_token;
+               packet->len = wq->name.len;
+               memcpy(packet->name, wq->name.name, wq->name.len);
+               packet->name[wq->name.len] = '\0';
+               packet->dev = wq->dev;
+               packet->ino = wq->ino;
+               packet->uid = from_kuid_munged(user_ns, wq->uid);
+               packet->gid = from_kgid_munged(user_ns, wq->gid);
+               packet->pid = wq->pid;
+               packet->tgid = wq->tgid;
+               break;
+       }
+       default:
+               pr_warn("bad type %d!\n", type);
+               mutex_unlock(&sbi->wq_mutex);
+               return;
+       }
+
+       pipe = get_file(sbi->pipe);
+
+       mutex_unlock(&sbi->wq_mutex);
+
+       switch (ret = autofs_write(sbi, pipe, &pkt, pktsz)) {
+       case 0:
+               break;
+       case -ENOMEM:
+       case -ERESTARTSYS:
+               /* Just fail this one */
+               autofs_wait_release(sbi, wq->wait_queue_token, ret);
+               break;
+       default:
+               autofs_catatonic_mode(sbi);
+               break;
+       }
+       fput(pipe);
+}
+
+static int autofs_getpath(struct autofs_sb_info *sbi,
+                         struct dentry *dentry, char *name)
+{
+       struct dentry *root = sbi->sb->s_root;
+       struct dentry *tmp;
+       char *buf;
+       char *p;
+       int len;
+       unsigned seq;
+
+rename_retry:
+       buf = name;
+       len = 0;
+
+       seq = read_seqbegin(&rename_lock);
+       rcu_read_lock();
+       spin_lock(&sbi->fs_lock);
+       for (tmp = dentry ; tmp != root ; tmp = tmp->d_parent)
+               len += tmp->d_name.len + 1;
+
+       if (!len || --len > NAME_MAX) {
+               spin_unlock(&sbi->fs_lock);
+               rcu_read_unlock();
+               if (read_seqretry(&rename_lock, seq))
+                       goto rename_retry;
+               return 0;
+       }
+
+       *(buf + len) = '\0';
+       p = buf + len - dentry->d_name.len;
+       strncpy(p, dentry->d_name.name, dentry->d_name.len);
+
+       for (tmp = dentry->d_parent; tmp != root ; tmp = tmp->d_parent) {
+               *(--p) = '/';
+               p -= tmp->d_name.len;
+               strncpy(p, tmp->d_name.name, tmp->d_name.len);
+       }
+       spin_unlock(&sbi->fs_lock);
+       rcu_read_unlock();
+       if (read_seqretry(&rename_lock, seq))
+               goto rename_retry;
+
+       return len;
+}
+
+static struct autofs_wait_queue *
+autofs_find_wait(struct autofs_sb_info *sbi, const struct qstr *qstr)
+{
+       struct autofs_wait_queue *wq;
+
+       for (wq = sbi->queues; wq; wq = wq->next) {
+               if (wq->name.hash == qstr->hash &&
+                   wq->name.len == qstr->len &&
+                   wq->name.name &&
+                   !memcmp(wq->name.name, qstr->name, qstr->len))
+                       break;
+       }
+       return wq;
+}
+
+/*
+ * Check if we have a valid request.
+ * Returns
+ * 1 if the request should continue.
+ *   In this case we can return an autofs_wait_queue entry if one is
+ *   found or NULL to idicate a new wait needs to be created.
+ * 0 or a negative errno if the request shouldn't continue.
+ */
+static int validate_request(struct autofs_wait_queue **wait,
+                           struct autofs_sb_info *sbi,
+                           const struct qstr *qstr,
+                           const struct path *path, enum autofs_notify notify)
+{
+       struct dentry *dentry = path->dentry;
+       struct autofs_wait_queue *wq;
+       struct autofs_info *ino;
+
+       if (sbi->catatonic)
+               return -ENOENT;
+
+       /* Wait in progress, continue; */
+       wq = autofs_find_wait(sbi, qstr);
+       if (wq) {
+               *wait = wq;
+               return 1;
+       }
+
+       *wait = NULL;
+
+       /* If we don't yet have any info this is a new request */
+       ino = autofs_dentry_ino(dentry);
+       if (!ino)
+               return 1;
+
+       /*
+        * If we've been asked to wait on an existing expire (NFY_NONE)
+        * but there is no wait in the queue ...
+        */
+       if (notify == NFY_NONE) {
+               /*
+                * Either we've betean the pending expire to post it's
+                * wait or it finished while we waited on the mutex.
+                * So we need to wait till either, the wait appears
+                * or the expire finishes.
+                */
+
+               while (ino->flags & AUTOFS_INF_EXPIRING) {
+                       mutex_unlock(&sbi->wq_mutex);
+                       schedule_timeout_interruptible(HZ/10);
+                       if (mutex_lock_interruptible(&sbi->wq_mutex))
+                               return -EINTR;
+
+                       if (sbi->catatonic)
+                               return -ENOENT;
+
+                       wq = autofs_find_wait(sbi, qstr);
+                       if (wq) {
+                               *wait = wq;
+                               return 1;
+                       }
+               }
+
+               /*
+                * Not ideal but the status has already gone. Of the two
+                * cases where we wait on NFY_NONE neither depend on the
+                * return status of the wait.
+                */
+               return 0;
+       }
+
+       /*
+        * If we've been asked to trigger a mount and the request
+        * completed while we waited on the mutex ...
+        */
+       if (notify == NFY_MOUNT) {
+               struct dentry *new = NULL;
+               struct path this;
+               int valid = 1;
+
+               /*
+                * If the dentry was successfully mounted while we slept
+                * on the wait queue mutex we can return success. If it
+                * isn't mounted (doesn't have submounts for the case of
+                * a multi-mount with no mount at it's base) we can
+                * continue on and create a new request.
+                */
+               if (!IS_ROOT(dentry)) {
+                       if (d_unhashed(dentry) &&
+                           d_really_is_positive(dentry)) {
+                               struct dentry *parent = dentry->d_parent;
+
+                               new = d_lookup(parent, &dentry->d_name);
+                               if (new)
+                                       dentry = new;
+                       }
+               }
+               this.mnt = path->mnt;
+               this.dentry = dentry;
+               if (path_has_submounts(&this))
+                       valid = 0;
+
+               if (new)
+                       dput(new);
+               return valid;
+       }
+
+       return 1;
+}
+
+int autofs_wait(struct autofs_sb_info *sbi,
+                const struct path *path, enum autofs_notify notify)
+{
+       struct dentry *dentry = path->dentry;
+       struct autofs_wait_queue *wq;
+       struct qstr qstr;
+       char *name;
+       int status, ret, type;
+       pid_t pid;
+       pid_t tgid;
+
+       /* In catatonic mode, we don't wait for nobody */
+       if (sbi->catatonic)
+               return -ENOENT;
+
+       /*
+        * Try translating pids to the namespace of the daemon.
+        *
+        * Zero means failure: we are in an unrelated pid namespace.
+        */
+       pid = task_pid_nr_ns(current, ns_of_pid(sbi->oz_pgrp));
+       tgid = task_tgid_nr_ns(current, ns_of_pid(sbi->oz_pgrp));
+       if (pid == 0 || tgid == 0)
+               return -ENOENT;
+
+       if (d_really_is_negative(dentry)) {
+               /*
+                * A wait for a negative dentry is invalid for certain
+                * cases. A direct or offset mount "always" has its mount
+                * point directory created and so the request dentry must
+                * be positive or the map key doesn't exist. The situation
+                * is very similar for indirect mounts except only dentrys
+                * in the root of the autofs file system may be negative.
+                */
+               if (autofs_type_trigger(sbi->type))
+                       return -ENOENT;
+               else if (!IS_ROOT(dentry->d_parent))
+                       return -ENOENT;
+       }
+
+       name = kmalloc(NAME_MAX + 1, GFP_KERNEL);
+       if (!name)
+               return -ENOMEM;
+
+       /* If this is a direct mount request create a dummy name */
+       if (IS_ROOT(dentry) && autofs_type_trigger(sbi->type))
+               qstr.len = sprintf(name, "%p", dentry);
+       else {
+               qstr.len = autofs_getpath(sbi, dentry, name);
+               if (!qstr.len) {
+                       kfree(name);
+                       return -ENOENT;
+               }
+       }
+       qstr.name = name;
+       qstr.hash = full_name_hash(dentry, name, qstr.len);
+
+       if (mutex_lock_interruptible(&sbi->wq_mutex)) {
+               kfree(qstr.name);
+               return -EINTR;
+       }
+
+       ret = validate_request(&wq, sbi, &qstr, path, notify);
+       if (ret <= 0) {
+               if (ret != -EINTR)
+                       mutex_unlock(&sbi->wq_mutex);
+               kfree(qstr.name);
+               return ret;
+       }
+
+       if (!wq) {
+               /* Create a new wait queue */
+               wq = kmalloc(sizeof(struct autofs_wait_queue), GFP_KERNEL);
+               if (!wq) {
+                       kfree(qstr.name);
+                       mutex_unlock(&sbi->wq_mutex);
+                       return -ENOMEM;
+               }
+
+               wq->wait_queue_token = autofs_next_wait_queue;
+               if (++autofs_next_wait_queue == 0)
+                       autofs_next_wait_queue = 1;
+               wq->next = sbi->queues;
+               sbi->queues = wq;
+               init_waitqueue_head(&wq->queue);
+               memcpy(&wq->name, &qstr, sizeof(struct qstr));
+               wq->dev = autofs_get_dev(sbi);
+               wq->ino = autofs_get_ino(sbi);
+               wq->uid = current_uid();
+               wq->gid = current_gid();
+               wq->pid = pid;
+               wq->tgid = tgid;
+               wq->status = -EINTR; /* Status return if interrupted */
+               wq->wait_ctr = 2;
+
+               if (sbi->version < 5) {
+                       if (notify == NFY_MOUNT)
+                               type = autofs_ptype_missing;
+                       else
+                               type = autofs_ptype_expire_multi;
+               } else {
+                       if (notify == NFY_MOUNT)
+                               type = autofs_type_trigger(sbi->type) ?
+                                       autofs_ptype_missing_direct :
+                                        autofs_ptype_missing_indirect;
+                       else
+                               type = autofs_type_trigger(sbi->type) ?
+                                       autofs_ptype_expire_direct :
+                                       autofs_ptype_expire_indirect;
+               }
+
+               pr_debug("new wait id = 0x%08lx, name = %.*s, nfy=%d\n",
+                        (unsigned long) wq->wait_queue_token, wq->name.len,
+                        wq->name.name, notify);
+
+               /*
+                * autofs_notify_daemon() may block; it will unlock ->wq_mutex
+                */
+               autofs_notify_daemon(sbi, wq, type);
+       } else {
+               wq->wait_ctr++;
+               pr_debug("existing wait id = 0x%08lx, name = %.*s, nfy=%d\n",
+                        (unsigned long) wq->wait_queue_token, wq->name.len,
+                        wq->name.name, notify);
+               mutex_unlock(&sbi->wq_mutex);
+               kfree(qstr.name);
+       }
+
+       /*
+        * wq->name.name is NULL iff the lock is already released
+        * or the mount has been made catatonic.
+        */
+       wait_event_killable(wq->queue, wq->name.name == NULL);
+       status = wq->status;
+
+       /*
+        * For direct and offset mounts we need to track the requester's
+        * uid and gid in the dentry info struct. This is so it can be
+        * supplied, on request, by the misc device ioctl interface.
+        * This is needed during daemon resatart when reconnecting
+        * to existing, active, autofs mounts. The uid and gid (and
+        * related string values) may be used for macro substitution
+        * in autofs mount maps.
+        */
+       if (!status) {
+               struct autofs_info *ino;
+               struct dentry *de = NULL;
+
+               /* direct mount or browsable map */
+               ino = autofs_dentry_ino(dentry);
+               if (!ino) {
+                       /* If not lookup actual dentry used */
+                       de = d_lookup(dentry->d_parent, &dentry->d_name);
+                       if (de)
+                               ino = autofs_dentry_ino(de);
+               }
+
+               /* Set mount requester */
+               if (ino) {
+                       spin_lock(&sbi->fs_lock);
+                       ino->uid = wq->uid;
+                       ino->gid = wq->gid;
+                       spin_unlock(&sbi->fs_lock);
+               }
+
+               if (de)
+                       dput(de);
+       }
+
+       /* Are we the last process to need status? */
+       mutex_lock(&sbi->wq_mutex);
+       if (!--wq->wait_ctr)
+               kfree(wq);
+       mutex_unlock(&sbi->wq_mutex);
+
+       return status;
+}
+
+
+int autofs_wait_release(struct autofs_sb_info *sbi,
+                       autofs_wqt_t wait_queue_token, int status)
+{
+       struct autofs_wait_queue *wq, **wql;
+
+       mutex_lock(&sbi->wq_mutex);
+       for (wql = &sbi->queues; (wq = *wql) != NULL; wql = &wq->next) {
+               if (wq->wait_queue_token == wait_queue_token)
+                       break;
+       }
+
+       if (!wq) {
+               mutex_unlock(&sbi->wq_mutex);
+               return -EINVAL;
+       }
+
+       *wql = wq->next;        /* Unlink from chain */
+       kfree(wq->name.name);
+       wq->name.name = NULL;   /* Do not wait on this queue */
+       wq->status = status;
+       wake_up(&wq->queue);
+       if (!--wq->wait_ctr)
+               kfree(wq);
+       mutex_unlock(&sbi->wq_mutex);
+
+       return 0;
+}
index 44727bf18297dfb9434b25bea2af72bfcbb8c2cc..99fda4d6da25aa81ce883a17f6182cf9e878d7c1 100644 (file)
@@ -1,5 +1,7 @@
 config AUTOFS4_FS
-       tristate "Kernel automounter version 4 support (also supports v3)"
+       tristate "Kernel automounter version 4 support (also supports v3 and v5)"
+       default n
+       depends on AUTOFS_FS = n
        help
          The automounter is a tool to automatically mount remote file systems
          on demand. This implementation is partially kernel-based to reduce
@@ -7,14 +9,38 @@ config AUTOFS4_FS
          automounter (amd), which is a pure user space daemon.
 
          To use the automounter you need the user-space tools from
-         <https://www.kernel.org/pub/linux/daemons/autofs/v4/>; you also
-         want to answer Y to "NFS file system support", below.
+         <https://www.kernel.org/pub/linux/daemons/autofs/>; you also want
+         to answer Y to "NFS file system support", below.
 
-         To compile this support as a module, choose M here: the module will be
-         called autofs4.  You will need to add "alias autofs autofs4" to your
-         modules configuration file.
+         This module is in the process of being renamed from autofs4 to
+         autofs. Since autofs is now the only module that provides the
+         autofs file system the module is not version 4 specific.
 
-         If you are not a part of a fairly large, distributed network or
-         don't have a laptop which needs to dynamically reconfigure to the
-         local network, you probably do not need an automounter, and can say
-         N here.
+         The autofs4 module is now built from the source located in
+         fs/autofs. The autofs4 directory and its configuration entry
+         will be removed two kernel versions from the inclusion of this
+         change.
+
+         Changes that will need to be made should be limited to:
+         - source include statments should be changed from autofs_fs4.h to
+           autofs_fs.h since these two header files have been merged.
+         - user space scripts that manually load autofs4.ko should be
+           changed to load autofs.ko. But since the module directory name
+           and the module name are the same as the file system name there
+           is no need to manually load module.
+         - any "alias autofs autofs4" will need to be removed.
+         - due to the autofs4 module directory name not being the same as
+           its file system name autoloading didn't work properly. Because
+           of this kernel configurations would often build the module into
+           the kernel. This may have resulted in selinux policies that will
+           prevent the autofs module from autoloading and will need to be
+           updated.
+
+         Please configure AUTOFS_FS instead of AUTOFS4_FS from now on.
+
+         NOTE: Since the modules autofs and autofs4 use the same file system
+               type name of "autofs" only one can be built. The "depends"
+               above will result in AUTOFS4_FS not appearing in .config for
+               any setting of AUTOFS_FS other than n and AUTOFS4_FS will
+               appear under the AUTOFS_FS entry otherwise which is intended
+               to draw attention to the module rename change.
index a811c1f7d9abda7c2be12071a63e0b0502adeb2e..417dd726d9ef6fea34afdc7972d34009ecf330b4 100644 (file)
@@ -4,4 +4,6 @@
 
 obj-$(CONFIG_AUTOFS4_FS) += autofs4.o
 
-autofs4-objs := init.o inode.o root.o symlink.o waitq.o expire.o dev-ioctl.o
+autofs4-objs := ../autofs/init.o ../autofs/inode.o ../autofs/root.o \
+       ../autofs/symlink.o ../autofs/waitq.o ../autofs/expire.o \
+       ../autofs/dev-ioctl.o
diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
deleted file mode 100644 (file)
index 4737615..0000000
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- *  Copyright 1997-1998 Transmeta Corporation - All Rights Reserved
- *  Copyright 2005-2006 Ian Kent <raven@themaw.net>
- *
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- */
-
-/* Internal header file for autofs */
-
-#include <linux/auto_fs4.h>
-#include <linux/auto_dev-ioctl.h>
-
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/time.h>
-#include <linux/string.h>
-#include <linux/wait.h>
-#include <linux/sched.h>
-#include <linux/mount.h>
-#include <linux/namei.h>
-#include <linux/uaccess.h>
-#include <linux/mutex.h>
-#include <linux/spinlock.h>
-#include <linux/list.h>
-#include <linux/completion.h>
-#include <asm/current.h>
-
-/* This is the range of ioctl() numbers we claim as ours */
-#define AUTOFS_IOC_FIRST     AUTOFS_IOC_READY
-#define AUTOFS_IOC_COUNT     32
-
-#define AUTOFS_DEV_IOCTL_IOC_FIRST     (AUTOFS_DEV_IOCTL_VERSION)
-#define AUTOFS_DEV_IOCTL_IOC_COUNT \
-       (AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD - AUTOFS_DEV_IOCTL_VERSION_CMD)
-
-#ifdef pr_fmt
-#undef pr_fmt
-#endif
-#define pr_fmt(fmt) KBUILD_MODNAME ":pid:%d:%s: " fmt, current->pid, __func__
-
-/*
- * Unified info structure.  This is pointed to by both the dentry and
- * inode structures.  Each file in the filesystem has an instance of this
- * structure.  It holds a reference to the dentry, so dentries are never
- * flushed while the file exists.  All name lookups are dealt with at the
- * dentry level, although the filesystem can interfere in the validation
- * process.  Readdir is implemented by traversing the dentry lists.
- */
-struct autofs_info {
-       struct dentry   *dentry;
-       struct inode    *inode;
-
-       int             flags;
-
-       struct completion expire_complete;
-
-       struct list_head active;
-       int active_count;
-
-       struct list_head expiring;
-
-       struct autofs_sb_info *sbi;
-       unsigned long last_used;
-       atomic_t count;
-
-       kuid_t uid;
-       kgid_t gid;
-};
-
-#define AUTOFS_INF_EXPIRING    (1<<0) /* dentry in the process of expiring */
-#define AUTOFS_INF_WANT_EXPIRE (1<<1) /* the dentry is being considered
-                                       * for expiry, so RCU_walk is
-                                       * not permitted.  If it progresses to
-                                       * actual expiry attempt, the flag is
-                                       * not cleared when EXPIRING is set -
-                                       * in that case it gets cleared only
-                                       * when it comes to clearing EXPIRING.
-                                       */
-#define AUTOFS_INF_PENDING     (1<<2) /* dentry pending mount */
-
-struct autofs_wait_queue {
-       wait_queue_head_t queue;
-       struct autofs_wait_queue *next;
-       autofs_wqt_t wait_queue_token;
-       /* We use the following to see what we are waiting for */
-       struct qstr name;
-       u32 dev;
-       u64 ino;
-       kuid_t uid;
-       kgid_t gid;
-       pid_t pid;
-       pid_t tgid;
-       /* This is for status reporting upon return */
-       int status;
-       unsigned int wait_ctr;
-};
-
-#define AUTOFS_SBI_MAGIC 0x6d4a556d
-
-struct autofs_sb_info {
-       u32 magic;
-       int pipefd;
-       struct file *pipe;
-       struct pid *oz_pgrp;
-       int catatonic;
-       int version;
-       int sub_version;
-       int min_proto;
-       int max_proto;
-       unsigned long exp_timeout;
-       unsigned int type;
-       struct super_block *sb;
-       struct mutex wq_mutex;
-       struct mutex pipe_mutex;
-       spinlock_t fs_lock;
-       struct autofs_wait_queue *queues; /* Wait queue pointer */
-       spinlock_t lookup_lock;
-       struct list_head active_list;
-       struct list_head expiring_list;
-       struct rcu_head rcu;
-};
-
-static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb)
-{
-       return (struct autofs_sb_info *)(sb->s_fs_info);
-}
-
-static inline struct autofs_info *autofs4_dentry_ino(struct dentry *dentry)
-{
-       return (struct autofs_info *)(dentry->d_fsdata);
-}
-
-/* autofs4_oz_mode(): do we see the man behind the curtain?  (The
- * processes which do manipulations for us in user space sees the raw
- * filesystem without "magic".)
- */
-static inline int autofs4_oz_mode(struct autofs_sb_info *sbi)
-{
-       return sbi->catatonic || task_pgrp(current) == sbi->oz_pgrp;
-}
-
-struct inode *autofs4_get_inode(struct super_block *, umode_t);
-void autofs4_free_ino(struct autofs_info *);
-
-/* Expiration */
-int is_autofs4_dentry(struct dentry *);
-int autofs4_expire_wait(const struct path *path, int rcu_walk);
-int autofs4_expire_run(struct super_block *, struct vfsmount *,
-                      struct autofs_sb_info *,
-                      struct autofs_packet_expire __user *);
-int autofs4_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
-                           struct autofs_sb_info *sbi, int when);
-int autofs4_expire_multi(struct super_block *, struct vfsmount *,
-                        struct autofs_sb_info *, int __user *);
-struct dentry *autofs4_expire_direct(struct super_block *sb,
-                                    struct vfsmount *mnt,
-                                    struct autofs_sb_info *sbi, int how);
-struct dentry *autofs4_expire_indirect(struct super_block *sb,
-                                      struct vfsmount *mnt,
-                                      struct autofs_sb_info *sbi, int how);
-
-/* Device node initialization */
-
-int autofs_dev_ioctl_init(void);
-void autofs_dev_ioctl_exit(void);
-
-/* Operations structures */
-
-extern const struct inode_operations autofs4_symlink_inode_operations;
-extern const struct inode_operations autofs4_dir_inode_operations;
-extern const struct file_operations autofs4_dir_operations;
-extern const struct file_operations autofs4_root_operations;
-extern const struct dentry_operations autofs4_dentry_operations;
-
-/* VFS automount flags management functions */
-static inline void __managed_dentry_set_managed(struct dentry *dentry)
-{
-       dentry->d_flags |= (DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT);
-}
-
-static inline void managed_dentry_set_managed(struct dentry *dentry)
-{
-       spin_lock(&dentry->d_lock);
-       __managed_dentry_set_managed(dentry);
-       spin_unlock(&dentry->d_lock);
-}
-
-static inline void __managed_dentry_clear_managed(struct dentry *dentry)
-{
-       dentry->d_flags &= ~(DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT);
-}
-
-static inline void managed_dentry_clear_managed(struct dentry *dentry)
-{
-       spin_lock(&dentry->d_lock);
-       __managed_dentry_clear_managed(dentry);
-       spin_unlock(&dentry->d_lock);
-}
-
-/* Initializing function */
-
-int autofs4_fill_super(struct super_block *, void *, int);
-struct autofs_info *autofs4_new_ino(struct autofs_sb_info *);
-void autofs4_clean_ino(struct autofs_info *);
-
-static inline int autofs_prepare_pipe(struct file *pipe)
-{
-       if (!(pipe->f_mode & FMODE_CAN_WRITE))
-               return -EINVAL;
-       if (!S_ISFIFO(file_inode(pipe)->i_mode))
-               return -EINVAL;
-       /* We want a packet pipe */
-       pipe->f_flags |= O_DIRECT;
-       return 0;
-}
-
-/* Queue management functions */
-
-int autofs4_wait(struct autofs_sb_info *,
-                const struct path *, enum autofs_notify);
-int autofs4_wait_release(struct autofs_sb_info *, autofs_wqt_t, int);
-void autofs4_catatonic_mode(struct autofs_sb_info *);
-
-static inline u32 autofs4_get_dev(struct autofs_sb_info *sbi)
-{
-       return new_encode_dev(sbi->sb->s_dev);
-}
-
-static inline u64 autofs4_get_ino(struct autofs_sb_info *sbi)
-{
-       return d_inode(sbi->sb->s_root)->i_ino;
-}
-
-static inline void __autofs4_add_expiring(struct dentry *dentry)
-{
-       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
-       struct autofs_info *ino = autofs4_dentry_ino(dentry);
-
-       if (ino) {
-               if (list_empty(&ino->expiring))
-                       list_add(&ino->expiring, &sbi->expiring_list);
-       }
-}
-
-static inline void autofs4_add_expiring(struct dentry *dentry)
-{
-       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
-       struct autofs_info *ino = autofs4_dentry_ino(dentry);
-
-       if (ino) {
-               spin_lock(&sbi->lookup_lock);
-               if (list_empty(&ino->expiring))
-                       list_add(&ino->expiring, &sbi->expiring_list);
-               spin_unlock(&sbi->lookup_lock);
-       }
-}
-
-static inline void autofs4_del_expiring(struct dentry *dentry)
-{
-       struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
-       struct autofs_info *ino = autofs4_dentry_ino(dentry);
-
-       if (ino) {
-               spin_lock(&sbi->lookup_lock);
-               if (!list_empty(&ino->expiring))
-                       list_del_init(&ino->expiring);
-               spin_unlock(&sbi->lookup_lock);
-       }
-}
-
-void autofs4_kill_sb(struct super_block *);
diff --git a/fs/autofs4/dev-ioctl.c b/fs/autofs4/dev-ioctl.c
deleted file mode 100644 (file)
index 26f6b4f..0000000
+++ /dev/null
@@ -1,761 +0,0 @@
-/*
- * Copyright 2008 Red Hat, Inc. All rights reserved.
- * Copyright 2008 Ian Kent <raven@themaw.net>
- *
- * This file is part of the Linux kernel and is made available under
- * the terms of the GNU General Public License, version 2, or at your
- * option, any later version, incorporated herein by reference.
- */
-
-#include <linux/module.h>
-#include <linux/vmalloc.h>
-#include <linux/miscdevice.h>
-#include <linux/init.h>
-#include <linux/wait.h>
-#include <linux/namei.h>
-#include <linux/fcntl.h>
-#include <linux/file.h>
-#include <linux/fdtable.h>
-#include <linux/sched.h>
-#include <linux/cred.h>
-#include <linux/compat.h>
-#include <linux/syscalls.h>
-#include <linux/magic.h>
-#include <linux/dcache.h>
-#include <linux/uaccess.h>
-#include <linux/slab.h>
-
-#include "autofs_i.h"
-
-/*
- * This module implements an interface for routing autofs ioctl control
- * commands via a miscellaneous device file.
- *
- * The alternate interface is needed because we need to be able open
- * an ioctl file descriptor on an autofs mount that may be covered by
- * another mount. This situation arises when starting automount(8)
- * or other user space daemon which uses direct mounts or offset
- * mounts (used for autofs lazy mount/umount of nested mount trees),
- * which have been left busy at at service shutdown.
- */
-
-typedef int (*ioctl_fn)(struct file *, struct autofs_sb_info *,
-                       struct autofs_dev_ioctl *);
-
-static int check_name(const char *name)
-{
-       if (!strchr(name, '/'))
-               return -EINVAL;
-       return 0;
-}
-
-/*
- * Check a string doesn't overrun the chunk of
- * memory we copied from user land.
- */
-static int invalid_str(char *str, size_t size)
-{
-       if (memchr(str, 0, size))
-               return 0;
-       return -EINVAL;
-}
-
-/*
- * Check that the user compiled against correct version of autofs
- * misc device code.
- *
- * As well as checking the version compatibility this always copies
- * the kernel interface version out.
- */
-static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param)
-{
-       int err = 0;
-
-       if ((param->ver_major != AUTOFS_DEV_IOCTL_VERSION_MAJOR) ||
-           (param->ver_minor > AUTOFS_DEV_IOCTL_VERSION_MINOR)) {
-               pr_warn("ioctl control interface version mismatch: "
-                       "kernel(%u.%u), user(%u.%u), cmd(0x%08x)\n",
-                       AUTOFS_DEV_IOCTL_VERSION_MAJOR,
-                       AUTOFS_DEV_IOCTL_VERSION_MINOR,
-                       param->ver_major, param->ver_minor, cmd);
-               err = -EINVAL;
-       }
-
-       /* Fill in the kernel version. */
-       param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR;
-       param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR;
-
-       return err;
-}
-
-/*
- * Copy parameter control struct, including a possible path allocated
- * at the end of the struct.
- */
-static struct autofs_dev_ioctl *
-copy_dev_ioctl(struct autofs_dev_ioctl __user *in)
-{
-       struct autofs_dev_ioctl tmp, *res;
-
-       if (copy_from_user(&tmp, in, AUTOFS_DEV_IOCTL_SIZE))
-               return ERR_PTR(-EFAULT);
-
-       if (tmp.size < AUTOFS_DEV_IOCTL_SIZE)
-               return ERR_PTR(-EINVAL);
-
-       if (tmp.size > AUTOFS_DEV_IOCTL_SIZE + PATH_MAX)
-               return ERR_PTR(-ENAMETOOLONG);
-
-       res = memdup_user(in, tmp.size);
-       if (!IS_ERR(res))
-               res->size = tmp.size;
-
-       return res;
-}
-
-static inline void free_dev_ioctl(struct autofs_dev_ioctl *param)
-{
-       kfree(param);
-}
-
-/*
- * Check sanity of parameter control fields and if a path is present
- * check that it is terminated and contains at least one "/".
- */
-static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param)
-{
-       int err;
-
-       err = check_dev_ioctl_version(cmd, param);
-       if (err) {
-               pr_warn("invalid device control module version "
-                       "supplied for cmd(0x%08x)\n", cmd);
-               goto out;
-       }
-
-       if (param->size > AUTOFS_DEV_IOCTL_SIZE) {
-               err = invalid_str(param->path, param->size - AUTOFS_DEV_IOCTL_SIZE);
-               if (err) {
-                       pr_warn(
-                         "path string terminator missing for cmd(0x%08x)\n",
-                         cmd);
-                       goto out;
-               }
-
-               err = check_name(param->path);
-               if (err) {
-                       pr_warn("invalid path supplied for cmd(0x%08x)\n",
-                               cmd);
-                       goto out;
-               }
-       }
-
-       err = 0;
-out:
-       return err;
-}
-
-/*
- * Get the autofs super block info struct from the file opened on
- * the autofs mount point.
- */
-static struct autofs_sb_info *autofs_dev_ioctl_sbi(struct file *f)
-{
-       struct autofs_sb_info *sbi = NULL;
-       struct inode *inode;
-
-       if (f) {
-               inode = file_inode(f);
-               sbi = autofs4_sbi(inode->i_sb);
-       }
-       return sbi;
-}
-
-/* Return autofs dev ioctl version */
-static int autofs_dev_ioctl_version(struct file *fp,
-                                   struct autofs_sb_info *sbi,
-                                   struct autofs_dev_ioctl *param)
-{
-       /* This should have already been set. */
-       param->ver_major = AUTOFS_DEV_IOCTL_VERSION_MAJOR;
-       param->ver_minor = AUTOFS_DEV_IOCTL_VERSION_MINOR;
-       return 0;
-}
-
-/* Return autofs module protocol version */
-static int autofs_dev_ioctl_protover(struct file *fp,
-                                    struct autofs_sb_info *sbi,
-                                    struct autofs_dev_ioctl *param)
-{
-       param->protover.version = sbi->version;
-       return 0;
-}
-
-/* Return autofs module protocol sub version */
-static int autofs_dev_ioctl_protosubver(struct file *fp,
-                                       struct autofs_sb_info *sbi,
-                                       struct autofs_dev_ioctl *param)
-{
-       param->protosubver.sub_version = sbi->sub_version;
-       return 0;
-}
-
-/* Find the topmost mount satisfying test() */
-static int find_autofs_mount(const char *pathname,
-                            struct path *res,
-                            int test(const struct path *path, void *data),
-                            void *data)
-{
-       struct path path;
-       int err;
-
-       err = kern_path_mountpoint(AT_FDCWD, pathname, &path, 0);
-       if (err)
-               return err;
-       err = -ENOENT;
-       while (path.dentry == path.mnt->mnt_root) {
-               if (path.dentry->d_sb->s_magic == AUTOFS_SUPER_MAGIC) {
-                       if (test(&path, data)) {
-                               path_get(&path);
-                               *res = path;
-                               err = 0;
-                               break;
-                       }
-               }
-               if (!follow_up(&path))
-                       break;
-       }
-       path_put(&path);
-       return err;
-}
-
-static int test_by_dev(const struct path *path, void *p)
-{
-       return path->dentry->d_sb->s_dev == *(dev_t *)p;
-}
-
-static int test_by_type(const struct path *path, void *p)
-{
-       struct autofs_info *ino = autofs4_dentry_ino(path->dentry);
-
-       return ino && ino->sbi->type & *(unsigned *)p;
-}
-
-/*
- * Open a file descriptor on the autofs mount point corresponding
- * to the given path and device number (aka. new_encode_dev(sb->s_dev)).
- */
-static int autofs_dev_ioctl_open_mountpoint(const char *name, dev_t devid)
-{
-       int err, fd;
-
-       fd = get_unused_fd_flags(O_CLOEXEC);
-       if (likely(fd >= 0)) {
-               struct file *filp;
-               struct path path;
-
-               err = find_autofs_mount(name, &path, test_by_dev, &devid);
-               if (err)
-                       goto out;
-
-               filp = dentry_open(&path, O_RDONLY, current_cred());
-               path_put(&path);
-               if (IS_ERR(filp)) {
-                       err = PTR_ERR(filp);
-                       goto out;
-               }
-
-               fd_install(fd, filp);
-       }
-
-       return fd;
-
-out:
-       put_unused_fd(fd);
-       return err;
-}
-
-/* Open a file descriptor on an autofs mount point */
-static int autofs_dev_ioctl_openmount(struct file *fp,
-                                     struct autofs_sb_info *sbi,
-                                     struct autofs_dev_ioctl *param)
-{
-       const char *path;
-       dev_t devid;
-       int err, fd;
-
-       /* param->path has already been checked */
-       if (!param->openmount.devid)
-               return -EINVAL;
-
-       param->ioctlfd = -1;
-
-       path = param->path;
-       devid = new_decode_dev(param->openmount.devid);
-
-       err = 0;
-       fd = autofs_dev_ioctl_open_mountpoint(path, devid);
-       if (unlikely(fd < 0)) {
-               err = fd;
-               goto out;
-       }
-
-       param->ioctlfd = fd;
-out:
-       return err;
-}
-
-/* Close file descriptor allocated above (user can also use close(2)). */
-static int autofs_dev_ioctl_closemount(struct file *fp,
-                                      struct autofs_sb_info *sbi,
-                                      struct autofs_dev_ioctl *param)
-{
-       return ksys_close(param->ioctlfd);
-}
-
-/*
- * Send "ready" status for an existing wait (either a mount or an expire
- * request).
- */
-static int autofs_dev_ioctl_ready(struct file *fp,
-                                 struct autofs_sb_info *sbi,
-                                 struct autofs_dev_ioctl *param)
-{
-       autofs_wqt_t token;
-
-       token = (autofs_wqt_t) param->ready.token;
-       return autofs4_wait_release(sbi, token, 0);
-}
-
-/*
- * Send "fail" status for an existing wait (either a mount or an expire
- * request).
- */
-static int autofs_dev_ioctl_fail(struct file *fp,
-                                struct autofs_sb_info *sbi,
-                                struct autofs_dev_ioctl *param)
-{
-       autofs_wqt_t token;
-       int status;
-
-       token = (autofs_wqt_t) param->fail.token;
-       status = param->fail.status < 0 ? param->fail.status : -ENOENT;
-       return autofs4_wait_release(sbi, token, status);
-}
-
-/*
- * Set the pipe fd for kernel communication to the daemon.
- *
- * Normally this is set at mount using an option but if we
- * are reconnecting to a busy mount then we need to use this
- * to tell the autofs mount about the new kernel pipe fd. In
- * order to protect mounts against incorrectly setting the
- * pipefd we also require that the autofs mount be catatonic.
- *
- * This also sets the process group id used to identify the
- * controlling process (eg. the owning automount(8) daemon).
- */
-static int autofs_dev_ioctl_setpipefd(struct file *fp,
-                                     struct autofs_sb_info *sbi,
-                                     struct autofs_dev_ioctl *param)
-{
-       int pipefd;
-       int err = 0;
-       struct pid *new_pid = NULL;
-
-       if (param->setpipefd.pipefd == -1)
-               return -EINVAL;
-
-       pipefd = param->setpipefd.pipefd;
-
-       mutex_lock(&sbi->wq_mutex);
-       if (!sbi->catatonic) {
-               mutex_unlock(&sbi->wq_mutex);
-               return -EBUSY;
-       } else {
-               struct file *pipe;
-
-               new_pid = get_task_pid(current, PIDTYPE_PGID);
-
-               if (ns_of_pid(new_pid) != ns_of_pid(sbi->oz_pgrp)) {
-                       pr_warn("not allowed to change PID namespace\n");
-                       err = -EINVAL;
-                       goto out;
-               }
-
-               pipe = fget(pipefd);
-               if (!pipe) {
-                       err = -EBADF;
-                       goto out;
-               }
-               if (autofs_prepare_pipe(pipe) < 0) {
-                       err = -EPIPE;
-                       fput(pipe);
-                       goto out;
-               }
-               swap(sbi->oz_pgrp, new_pid);
-               sbi->pipefd = pipefd;
-               sbi->pipe = pipe;
-               sbi->catatonic = 0;
-       }
-out:
-       put_pid(new_pid);
-       mutex_unlock(&sbi->wq_mutex);
-       return err;
-}
-
-/*
- * Make the autofs mount point catatonic, no longer responsive to
- * mount requests. Also closes the kernel pipe file descriptor.
- */
-static int autofs_dev_ioctl_catatonic(struct file *fp,
-                                     struct autofs_sb_info *sbi,
-                                     struct autofs_dev_ioctl *param)
-{
-       autofs4_catatonic_mode(sbi);
-       return 0;
-}
-
-/* Set the autofs mount timeout */
-static int autofs_dev_ioctl_timeout(struct file *fp,
-                                   struct autofs_sb_info *sbi,
-                                   struct autofs_dev_ioctl *param)
-{
-       unsigned long timeout;
-
-       timeout = param->timeout.timeout;
-       param->timeout.timeout = sbi->exp_timeout / HZ;
-       sbi->exp_timeout = timeout * HZ;
-       return 0;
-}
-
-/*
- * Return the uid and gid of the last request for the mount
- *
- * When reconstructing an autofs mount tree with active mounts
- * we need to re-connect to mounts that may have used the original
- * process uid and gid (or string variations of them) for mount
- * lookups within the map entry.
- */
-static int autofs_dev_ioctl_requester(struct file *fp,
-                                     struct autofs_sb_info *sbi,
-                                     struct autofs_dev_ioctl *param)
-{
-       struct autofs_info *ino;
-       struct path path;
-       dev_t devid;
-       int err = -ENOENT;
-
-       if (param->size <= AUTOFS_DEV_IOCTL_SIZE) {
-               err = -EINVAL;
-               goto out;
-       }
-
-       devid = sbi->sb->s_dev;
-
-       param->requester.uid = param->requester.gid = -1;
-
-       err = find_autofs_mount(param->path, &path, test_by_dev, &devid);
-       if (err)
-               goto out;
-
-       ino = autofs4_dentry_ino(path.dentry);
-       if (ino) {
-               err = 0;
-               autofs4_expire_wait(&path, 0);
-               spin_lock(&sbi->fs_lock);
-               param->requester.uid =
-                       from_kuid_munged(current_user_ns(), ino->uid);
-               param->requester.gid =
-                       from_kgid_munged(current_user_ns(), ino->gid);
-               spin_unlock(&sbi->fs_lock);
-       }
-       path_put(&path);
-out:
-       return err;
-}
-
-/*
- * Call repeatedly until it returns -EAGAIN, meaning there's nothing
- * more that can be done.
- */
-static int autofs_dev_ioctl_expire(struct file *fp,
-                                  struct autofs_sb_info *sbi,
-                                  struct autofs_dev_ioctl *param)
-{
-       struct vfsmount *mnt;
-       int how;
-
-       how = param->expire.how;
-       mnt = fp->f_path.mnt;
-
-       return autofs4_do_expire_multi(sbi->sb, mnt, sbi, how);
-}
-
-/* Check if autofs mount point is in use */
-static int autofs_dev_ioctl_askumount(struct file *fp,
-                                     struct autofs_sb_info *sbi,
-                                     struct autofs_dev_ioctl *param)
-{
-       param->askumount.may_umount = 0;
-       if (may_umount(fp->f_path.mnt))
-               param->askumount.may_umount = 1;
-       return 0;
-}
-
-/*
- * Check if the given path is a mountpoint.
- *
- * If we are supplied with the file descriptor of an autofs
- * mount we're looking for a specific mount. In this case
- * the path is considered a mountpoint if it is itself a
- * mountpoint or contains a mount, such as a multi-mount
- * without a root mount. In this case we return 1 if the
- * path is a mount point and the super magic of the covering
- * mount if there is one or 0 if it isn't a mountpoint.
- *
- * If we aren't supplied with a file descriptor then we
- * lookup the path and check if it is the root of a mount.
- * If a type is given we are looking for a particular autofs
- * mount and if we don't find a match we return fail. If the
- * located path is the root of a mount we return 1 along with
- * the super magic of the mount or 0 otherwise.
- *
- * In both cases the the device number (as returned by
- * new_encode_dev()) is also returned.
- */
-static int autofs_dev_ioctl_ismountpoint(struct file *fp,
-                                        struct autofs_sb_info *sbi,
-                                        struct autofs_dev_ioctl *param)
-{
-       struct path path;
-       const char *name;
-       unsigned int type;
-       unsigned int devid, magic;
-       int err = -ENOENT;
-
-       if (param->size <= AUTOFS_DEV_IOCTL_SIZE) {
-               err = -EINVAL;
-               goto out;
-       }
-
-       name = param->path;
-       type = param->ismountpoint.in.type;
-
-       param->ismountpoint.out.devid = devid = 0;
-       param->ismountpoint.out.magic = magic = 0;
-
-       if (!fp || param->ioctlfd == -1) {
-               if (autofs_type_any(type))
-                       err = kern_path_mountpoint(AT_FDCWD,
-                                                  name, &path, LOOKUP_FOLLOW);
-               else
-                       err = find_autofs_mount(name, &path,
-                                               test_by_type, &type);
-               if (err)
-                       goto out;
-               devid = new_encode_dev(path.dentry->d_sb->s_dev);
-               err = 0;
-               if (path.mnt->mnt_root == path.dentry) {
-                       err = 1;
-                       magic = path.dentry->d_sb->s_magic;
-               }
-       } else {
-               dev_t dev = sbi->sb->s_dev;
-
-               err = find_autofs_mount(name, &path, test_by_dev, &dev);
-               if (err)
-                       goto out;
-
-               devid = new_encode_dev(dev);
-
-               err = path_has_submounts(&path);
-
-               if (follow_down_one(&path))
-                       magic = path.dentry->d_sb->s_magic;
-       }
-
-       param->ismountpoint.out.devid = devid;
-       param->ismountpoint.out.magic = magic;
-       path_put(&path);
-out:
-       return err;
-}
-
-/*
- * Our range of ioctl numbers isn't 0 based so we need to shift
- * the array index by _IOC_NR(AUTOFS_CTL_IOC_FIRST) for the table
- * lookup.
- */
-#define cmd_idx(cmd)   (cmd - _IOC_NR(AUTOFS_DEV_IOCTL_IOC_FIRST))
-
-static ioctl_fn lookup_dev_ioctl(unsigned int cmd)
-{
-       static ioctl_fn _ioctls[] = {
-               autofs_dev_ioctl_version,
-               autofs_dev_ioctl_protover,
-               autofs_dev_ioctl_protosubver,
-               autofs_dev_ioctl_openmount,
-               autofs_dev_ioctl_closemount,
-               autofs_dev_ioctl_ready,
-               autofs_dev_ioctl_fail,
-               autofs_dev_ioctl_setpipefd,
-               autofs_dev_ioctl_catatonic,
-               autofs_dev_ioctl_timeout,
-               autofs_dev_ioctl_requester,
-               autofs_dev_ioctl_expire,
-               autofs_dev_ioctl_askumount,
-               autofs_dev_ioctl_ismountpoint,
-       };
-       unsigned int idx = cmd_idx(cmd);
-
-       return (idx >= ARRAY_SIZE(_ioctls)) ? NULL : _ioctls[idx];
-}
-
-/* ioctl dispatcher */
-static int _autofs_dev_ioctl(unsigned int command,
-