root/fs/autofs/expire.c
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright 1997-1998 Transmeta Corporation -- All Rights Reserved
 * Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
 * Copyright 2001-2006 Ian Kent <raven@themaw.net>
 */

#include "autofs_i.h"

/* Check if a dentry can be expired */
static inline int autofs_can_expire(struct dentry *dentry,
                                    unsigned long timeout, unsigned int how)
{
        struct autofs_info *ino = autofs_dentry_ino(dentry);

        /* dentry in the process of being deleted */
        if (ino == NULL)
                return 0;

        if (!(how & AUTOFS_EXP_IMMEDIATE)) {
                /* Too young to die */
                if (!timeout || time_after(ino->last_used + timeout, jiffies))
                        return 0;
        }
        return 1;
}

/* Check a mount point for busyness */
static int autofs_mount_busy(struct vfsmount *mnt,
                             struct dentry *dentry, unsigned int how)
{
        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;
        }

        /* Not a submount, has a forced expire been requested */
        if (how & AUTOFS_EXP_FORCED) {
                status = 0;
                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;
}

/* p->d_lock held */
static struct dentry *positive_after(struct dentry *p, struct dentry *child)
{
        child = child ? d_next_sibling(child) : d_first_child(p);

        hlist_for_each_entry_from(child, d_sib) {
                spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED);
                if (simple_positive(child)) {
                        dget_dlock(child);
                        spin_unlock(&child->d_lock);
                        return child;
                }
                spin_unlock(&child->d_lock);
        }

        return NULL;
}

/*
 * 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 dentry *q;

        spin_lock(&sbi->lookup_lock);
        spin_lock(&root->d_lock);
        q = positive_after(root, prev);
        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 dentry *p = prev, *ret = NULL, *d = NULL;

        if (prev == NULL)
                return dget(root);

        spin_lock(&sbi->lookup_lock);
        spin_lock(&p->d_lock);
        while (1) {
                struct dentry *parent;

                ret = positive_after(p, d);
                if (ret || p == root)
                        break;
                parent = p->d_parent;
                spin_unlock(&p->d_lock);
                spin_lock(&parent->d_lock);
                d = p;
                p = parent;
        }
        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,
                              unsigned int how)
{
        pr_debug("top %p %pd\n", top, top);

        /* Forced expire, user space handles busy mounts */
        if (how & AUTOFS_EXP_FORCED)
                return 0;

        /* 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, how))
                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,
                            unsigned int how)
{
        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, how)) {
                                top_ino->last_used = jiffies;
                                dput(p);
                                return 1;
                        }
                } else {
                        struct autofs_info *ino = autofs_dentry_ino(p);
                        unsigned int ino_count = READ_ONCE(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;
                        }
                }
        }

        /* Forced expire, user space handles busy mounts */
        if (how & AUTOFS_EXP_FORCED)
                return 0;

        /* Timeout of a tree mount is ultimately determined by its top dentry */
        if (!autofs_can_expire(top, timeout, how))
                return 1;

        return 0;
}

static struct dentry *autofs_check_leaves(struct vfsmount *mnt,
                                          struct dentry *parent,
                                          unsigned long timeout,
                                          unsigned int how)
{
        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, how))
                                continue;

                        /* This isn't a submount so if a forced expire
                         * has been requested, user space handles busy
                         * mounts */
                        if (how & AUTOFS_EXP_FORCED)
                                return p;

                        /* Can we expire this guy */
                        if (autofs_can_expire(p, timeout, how))
                                return p;
                }
        }
        return NULL;
}

/* Check if we can expire a direct mount (possibly a tree) */
static struct dentry *autofs_expire_direct(struct super_block *sb,
                                           struct vfsmount *mnt,
                                           struct autofs_sb_info *sbi,
                                           unsigned int how)
{
        struct dentry *root = dget(sb->s_root);
        struct autofs_info *ino;
        unsigned long timeout;

        if (!root)
                return NULL;

        timeout = sbi->exp_timeout;

        if (!autofs_direct_busy(mnt, root, timeout, how)) {
                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, how)) {
                        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,
                                    unsigned int how)
{
        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, how))
                        return NULL;

                /* This isn't a submount so if a forced expire
                 * has been requested, user space handles busy
                 * mounts */
                if (how & AUTOFS_EXP_FORCED)
                        return dentry;

                /* Can we expire this guy */
                if (autofs_can_expire(dentry, timeout, how))
                        return dentry;
                return NULL;
        }

        if (d_is_symlink(dentry)) {
                pr_debug("checking symlink %p %pd\n", dentry, dentry);

                /* Forced expire, user space handles busy mounts */
                if (how & AUTOFS_EXP_FORCED)
                        return 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, how))
                        return dentry;
                return NULL;
        }

        if (autofs_empty(ino))
                return NULL;

        /* Case 2: tree mount, expire iff entire tree is not busy */
        if (!(how & AUTOFS_EXP_LEAVES)) {
                /* Not a forced expire? */
                if (!(how & AUTOFS_EXP_FORCED)) {
                        /* ref-walk currently on this dentry? */
                        ino_count = READ_ONCE(ino->count) + 1;
                        if (d_count(dentry) > ino_count)
                                return NULL;
                }

                if (!autofs_tree_busy(mnt, dentry, timeout, how))
                        return dentry;
        /*
         * Case 3: pseudo direct mount, expire individual leaves
         *         (autofs-4.1).
         */
        } else {
                struct dentry *expired;

                /* Not a forced expire? */
                if (!(how & AUTOFS_EXP_FORCED)) {
                        /* ref-walk currently on this dentry? */
                        ino_count = READ_ONCE(ino->count) + 1;
                        if (d_count(dentry) > ino_count)
                                return NULL;
                }

                expired = autofs_check_leaves(mnt, dentry, timeout, how);
                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
 */
static struct dentry *autofs_expire_indirect(struct super_block *sb,
                                             struct vfsmount *mnt,
                                             struct autofs_sb_info *sbi,
                                             unsigned 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;

        dentry = NULL;
        while ((dentry = get_next_positive_subdir(dentry, root))) {
                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);

                if (ino->flags & AUTOFS_INF_EXPIRE_SET)
                        timeout = ino->exp_timeout;
                else
                        timeout = sbi->exp_timeout;

                expired = should_expire(dentry, mnt, timeout, how);
                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.
                 */
                how &= ~AUTOFS_EXP_LEAVES;
                found = should_expire(expired, mnt, timeout, how);
                if (found != expired) { // something has changed, continue
                        dput(found);
                        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';

        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 = jiffies;
        ino->flags &= ~(AUTOFS_INF_EXPIRING|AUTOFS_INF_WANT_EXPIRE);
        complete_all(&ino->expire_complete);
        spin_unlock(&sbi->fs_lock);

        dput(dentry);

        return ret;
}

int autofs_do_expire_multi(struct super_block *sb, struct vfsmount *mnt,
                           struct autofs_sb_info *sbi, unsigned int how)
{
        struct dentry *dentry;
        int ret = -EAGAIN;

        if (autofs_type_trigger(sbi->type))
                dentry = autofs_expire_direct(sb, mnt, sbi, how);
        else
                dentry = autofs_expire_indirect(sb, mnt, sbi, how);

        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 = jiffies;
                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)
{
        unsigned int how = 0;

        if (arg && get_user(how, arg))
                return -EFAULT;

        return autofs_do_expire_multi(sb, mnt, sbi, how);
}