/* Check if a dentry can be expired */ static inline int autofs4_can_expire(struct dentry *dentry, unsigned long timeout, int do_now) { struct autofs_info *ino = autofs4_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; /* update last_used here :- - obviously makes sense if it is in use now - less obviously, prevents rapid-fire expire attempts if expire fails the first time */ ino->last_used = now; } return 1; }
/* * 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 autofs4_direct_busy(struct vfsmount *mnt, struct dentry *top, unsigned long timeout, int do_now) { DPRINTK("top %p %.*s", top, (int) top->d_name.len, top->d_name.name); /* If it's busy update the expiry counters */ if (!may_umount_tree(mnt)) { struct autofs_info *ino = autofs4_dentry_ino(top); if (ino) ino->last_used = jiffies; return 1; } /* Timeout of a direct mount is determined by its top dentry */ if (!autofs4_can_expire(top, timeout, do_now)) return 1; return 0; }
int autofs4_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 = autofs4_expire_direct(sb, mnt, sbi, when); else dentry = autofs4_expire_indirect(sb, mnt, sbi, when); if (dentry) { struct autofs_info *ino = autofs4_dentry_ino(dentry); /* This is synchronous because it makes the daemon a little easier */ ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); spin_lock(&sbi->fs_lock); ino->flags &= ~AUTOFS_INF_EXPIRING; spin_lock(&dentry->d_lock); if (ret) __managed_dentry_clear_transit(dentry); else { if ((IS_ROOT(dentry) || (autofs_type_indirect(sbi->type) && IS_ROOT(dentry->d_parent))) && !(dentry->d_flags & DCACHE_NEED_AUTOMOUNT)) __managed_dentry_set_automount(dentry); } spin_unlock(&dentry->d_lock); complete_all(&ino->expire_complete); spin_unlock(&sbi->fs_lock); dput(dentry); } return ret; }
static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) { struct autofs_sb_info *sbi = autofs4_sbi(dir->i_sb); struct autofs_info *ino = autofs4_dentry_ino(dentry); struct inode *inode; lock_kernel(); if ( !autofs4_oz_mode(sbi) ) { unlock_kernel(); return -EACCES; } DPRINTK(("autofs_dir_mkdir: dentry %p, creating %.*s\n", dentry, dentry->d_name.len, dentry->d_name.name)); ino = autofs4_init_ino(ino, sbi, S_IFDIR | 0555); if (ino == NULL) { unlock_kernel(); return -ENOSPC; } inode = autofs4_get_inode(dir->i_sb, ino); d_instantiate(dentry, inode); if (dir == dir->i_sb->s_root->d_inode) dentry->d_op = &autofs4_root_dentry_operations; else dentry->d_op = &autofs4_dentry_operations; dentry->d_fsdata = ino; ino->dentry = dget(dentry); ino->inode = inode; dir->i_nlink++; dir->i_mtime = CURRENT_TIME; unlock_kernel(); return 0; }
/* Call repeatedly until it returns -EAGAIN, meaning there's nothing more to be done */ int autofs4_expire_multi(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, int __user *arg) { struct dentry *dentry; int ret = -EAGAIN; int do_now = 0; if (arg && get_user(do_now, arg)) return -EFAULT; if ((dentry = autofs4_expire(sb, mnt, sbi, do_now)) != NULL) { struct autofs_info *de_info = autofs4_dentry_ino(dentry); /* This is synchronous because it makes the daemon a little easier */ de_info->flags |= AUTOFS_INF_EXPIRING; ret = autofs4_wait(sbi, dentry, NFY_EXPIRE); de_info->flags &= ~AUTOFS_INF_EXPIRING; dput(dentry); } return ret; }
/* * 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 <= sizeof(*param)) { 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; }
/* Perform an expiry operation */ int autofs4_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 = autofs4_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 = autofs4_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; }
/* Perform an expiry operation */ int autofs4_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; if ((dentry = autofs4_expire_indirect(sb, mnt, sbi, 0)) == NULL) 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 = autofs4_dentry_ino(dentry); ino->flags &= ~AUTOFS_INF_EXPIRING; if (!d_unhashed(dentry)) managed_dentry_clear_transit(dentry); complete_all(&ino->expire_complete); spin_unlock(&sbi->fs_lock); return ret; }
/* Check a mount point for busyness */ static int autofs4_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_autofs4_dentry(path.dentry)) { struct autofs_sb_info *sbi = autofs4_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 = autofs4_dentry_ino(top); ino->last_used = jiffies; goto done; } status = 0; done: pr_debug("returning = %d\n", status); path_put(&path); return status; }
/* * 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 *autofs4_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 *expired = NULL; struct list_head *next; int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; struct autofs_info *ino; unsigned int ino_count; if (!root) return NULL; now = jiffies; timeout = sbi->exp_timeout; spin_lock(&dcache_lock); next = root->d_subdirs.next; /* On exit from the loop expire is set to a dgot dentry * to expire or it's NULL */ while ( next != &root->d_subdirs ) { struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if (!simple_positive(dentry)) { next = next->next; continue; } dentry = dget(dentry); spin_unlock(&dcache_lock); spin_lock(&sbi->fs_lock); ino = autofs4_dentry_ino(dentry); /* * 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)) { DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 2; if (atomic_read(&dentry->d_count) > ino_count) goto next; /* Can we umount this guy */ if (autofs4_mount_busy(mnt, dentry)) goto next; /* Can we expire this guy */ if (autofs4_can_expire(dentry, timeout, do_now)) { expired = dentry; goto found; } goto next; } if (simple_empty(dentry)) goto next; /* 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 (atomic_read(&dentry->d_count) > ino_count) goto next; if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { expired = dentry; goto found; } /* * Case 3: pseudo direct mount, expire individual leaves * (autofs-4.1). */ } else { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; if (atomic_read(&dentry->d_count) > ino_count) goto next; expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { dput(dentry); goto found; } } next: spin_unlock(&sbi->fs_lock); dput(dentry); spin_lock(&dcache_lock); next = next->next; } spin_unlock(&dcache_lock); return NULL; found: DPRINTK("returning %p %.*s", expired, (int)expired->d_name.len, expired->d_name.name); ino = autofs4_dentry_ino(expired); ino->flags |= AUTOFS_INF_EXPIRING; init_completion(&ino->expire_complete); spin_unlock(&sbi->fs_lock); spin_lock(&dcache_lock); list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); spin_unlock(&dcache_lock); return expired; }
/* * 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 *autofs4_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 = autofs4_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 = autofs4_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; }
/* 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 = autofs4_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 (autofs4_mount_busy(mnt, dentry)) return NULL; /* Can we expire this guy */ if (autofs4_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 (autofs4_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 (!autofs4_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 = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { if (expired == dentry) dput(dentry); return expired; } } return NULL; }
struct dentry *autofs4_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 = NULL; int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; struct autofs_info *ino; unsigned int ino_count; if (!root) return NULL; now = jiffies; timeout = sbi->exp_timeout; dentry = NULL; while ((dentry = get_next_positive_subdir(dentry, root))) { spin_lock(&sbi->fs_lock); ino = autofs4_dentry_ino(dentry); if (ino->flags & AUTOFS_INF_PENDING) goto next; if (d_mountpoint(dentry)) { DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); ino_count = atomic_read(&ino->count) + 2; if (dentry->d_count > ino_count) goto next; if (autofs4_mount_busy(mnt, dentry)) goto next; if (autofs4_can_expire(dentry, timeout, do_now)) { expired = dentry; goto found; } goto next; } if (simple_empty(dentry)) goto next; if (!exp_leaves) { ino_count = atomic_read(&ino->count) + 1; if (dentry->d_count > ino_count) goto next; if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { expired = dentry; goto found; } } else { ino_count = atomic_read(&ino->count) + 1; if (dentry->d_count > ino_count) goto next; expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { dput(dentry); goto found; } } next: spin_unlock(&sbi->fs_lock); } return NULL; found: DPRINTK("returning %p %.*s", expired, (int)expired->d_name.len, expired->d_name.name); ino = autofs4_dentry_ino(expired); ino->flags |= AUTOFS_INF_EXPIRING; init_completion(&ino->expire_complete); spin_unlock(&sbi->fs_lock); spin_lock(&sbi->lookup_lock); spin_lock(&expired->d_parent->d_lock); spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED); list_move(&expired->d_parent->d_subdirs, &expired->d_u.d_child); spin_unlock(&expired->d_lock); spin_unlock(&expired->d_parent->d_lock); spin_unlock(&sbi->lookup_lock); return expired; }
/* Check a directory tree of mount points for busyness * The tree is not busy iff no mountpoints are busy */ static int autofs4_tree_busy(struct vfsmount *mnt, struct dentry *top, unsigned long timeout, int do_now) { struct autofs_info *top_ino = autofs4_dentry_ino(top); struct dentry *p; DPRINTK("top %p %.*s", top, (int)top->d_name.len, top->d_name.name); /* Negative dentry - give up */ if (!simple_positive(top)) return 1; p = NULL; while ((p = get_next_positive_dentry(p, top))) { DPRINTK("dentry %p %.*s", p, (int) p->d_name.len, p->d_name.name); /* * 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 (autofs4_mount_busy(mnt, p)) { top_ino->last_used = jiffies; dput(p); return 1; } } else { struct autofs_info *ino = autofs4_dentry_ino(p); unsigned int ino_count = atomic_read(&ino->count); /* * Clean stale dentries below that have not been * invalidated after a mount fail during lookup */ d_invalidate(p); /* allow for dget above and top is already dgot */ if (p == top) ino_count += 2; else ino_count++; if (p->d_count > ino_count) { top_ino->last_used = jiffies; dput(p); return 1; } } } /* Timeout of a tree mount is ultimately determined by its top dentry */ if (!autofs4_can_expire(top, timeout, do_now)) return 1; return 0; }
/* * 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 *autofs4_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 = NULL; int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; struct autofs_info *ino; unsigned int ino_count; if (!root) return NULL; now = jiffies; timeout = sbi->exp_timeout; dentry = NULL; while ((dentry = get_next_positive_subdir(dentry, root))) { spin_lock(&sbi->fs_lock); ino = autofs4_dentry_ino(dentry); /* No point expiring a pending mount */ if (ino->flags & AUTOFS_INF_PENDING) goto next; /* * 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)) { DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 2; if (dentry->d_count > ino_count) goto next; /* Can we umount this guy */ if (autofs4_mount_busy(mnt, dentry)) goto next; /* Can we expire this guy */ if (autofs4_can_expire(dentry, timeout, do_now)) { expired = dentry; goto found; } goto next; } if (simple_empty(dentry)) goto next; /* 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 (dentry->d_count > ino_count) goto next; if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) { expired = dentry; goto found; } /* * Case 3: pseudo direct mount, expire individual leaves * (autofs-4.1). */ } else { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; if (dentry->d_count > ino_count) goto next; expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { dput(dentry); goto found; } } next: spin_unlock(&sbi->fs_lock); } return NULL; found: DPRINTK("returning %p %.*s", expired, (int)expired->d_name.len, expired->d_name.name); ino = autofs4_dentry_ino(expired); ino->flags |= AUTOFS_INF_EXPIRING; init_completion(&ino->expire_complete); spin_unlock(&sbi->fs_lock); spin_lock(&sbi->lookup_lock); spin_lock(&expired->d_parent->d_lock); spin_lock_nested(&expired->d_lock, DENTRY_D_LOCK_NESTED); list_move(&expired->d_parent->d_subdirs, &expired->d_child); spin_unlock(&expired->d_lock); spin_unlock(&expired->d_parent->d_lock); spin_unlock(&sbi->lookup_lock); return expired; }
static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, struct autofs_sb_info *sbi, int flags) { struct autofs_info *de_info = autofs4_dentry_ino(dentry); int status = 0; /* Block on any pending expiry here; invalidate the dentry when expiration is done to trigger mount request with a new dentry */ if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { DPRINTK("waiting for expire %p name=%.*s", dentry, dentry->d_name.len, dentry->d_name.name); status = autofs4_wait(sbi, dentry, NFY_NONE); DPRINTK("expire done status=%d", status); return 0; } DPRINTK("dentry=%p %.*s ino=%p", dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode); /* Wait for a pending mount, triggering one if there isn't one already */ if (dentry->d_inode == NULL) { DPRINTK("waiting for mount name=%.*s", dentry->d_name.len, dentry->d_name.name); status = autofs4_wait(sbi, dentry, NFY_MOUNT); DPRINTK("mount done status=%d", status); if (status && dentry->d_inode) return 0; /* Try to get the kernel to invalidate this dentry */ /* Turn this into a real negative dentry? */ if (status == -ENOENT) { dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); return 1; } else if (status) { /* Return a negative dentry, but leave it "pending" */ return 1; } /* Trigger mount for path component or follow link */ } else if (flags & (LOOKUP_CONTINUE | LOOKUP_DIRECTORY) || current->link_count) { DPRINTK("waiting for mount name=%.*s", dentry->d_name.len, dentry->d_name.name); spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); status = autofs4_wait(sbi, dentry, NFY_MOUNT); DPRINTK("mount done status=%d", status); if (status) { spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); return 0; } } /* We don't update the usages for the autofs daemon itself, this is necessary for recursive autofs mounts */ if (!autofs4_oz_mode(sbi)) autofs4_update_usage(dentry); spin_lock(&dentry->d_lock); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); return 1; }
/* Check a directory tree of mount points for busyness * The tree is not busy iff no mountpoints are busy */ static int autofs4_tree_busy(struct vfsmount *mnt, struct dentry *top, unsigned long timeout, int do_now) { struct autofs_info *top_ino = autofs4_dentry_ino(top); struct dentry *p; DPRINTK("top %p %.*s", top, (int)top->d_name.len, top->d_name.name); /* Negative dentry - give up */ if (!simple_positive(top)) return 1; spin_lock(&dcache_lock); for (p = top; p; p = next_dentry(p, top)) { /* Negative dentry - give up */ if (!simple_positive(p)) continue; DPRINTK("dentry %p %.*s", p, (int) p->d_name.len, p->d_name.name); p = dget(p); spin_unlock(&dcache_lock); /* * 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 (autofs4_mount_busy(mnt, p)) { top_ino->last_used = jiffies; dput(p); return 1; } } else { struct autofs_info *ino = autofs4_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 (atomic_read(&p->d_count) > ino_count) { top_ino->last_used = jiffies; dput(p); return 1; } } dput(p); spin_lock(&dcache_lock); } spin_unlock(&dcache_lock); /* Timeout of a tree mount is ultimately determined by its top dentry */ if (!autofs4_can_expire(top, timeout, do_now)) return 1; return 0; }
/* * 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 *autofs4_expire(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 *expired = NULL; struct list_head *next; int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; if ( !sbi->exp_timeout || !root ) return NULL; now = jiffies; timeout = sbi->exp_timeout; spin_lock(&dcache_lock); next = root->d_subdirs.next; /* On exit from the loop expire is set to a dgot dentry * to expire or it's NULL */ while ( next != &root->d_subdirs ) { struct dentry *dentry = list_entry(next, struct dentry, d_u.d_child); /* Negative dentry - give up */ if ( !simple_positive(dentry) ) { next = next->next; continue; } dentry = dget(dentry); spin_unlock(&dcache_lock); /* Case 1: indirect mount or top level direct mount */ if (d_mountpoint(dentry)) { DPRINTK("checking mountpoint %p %.*s", dentry, (int)dentry->d_name.len, dentry->d_name.name); /* Can we expire this guy */ if (!autofs4_can_expire(dentry, timeout, do_now)) goto next; /* Can we umount this guy */ if (autofs4_check_mount(mnt, dentry)) { expired = dentry; break; } goto next; } if ( simple_empty(dentry) ) goto next; /* Case 2: tree mount, expire iff entire tree is not busy */ if (!exp_leaves) { /* Lock the tree as we must expire as a whole */ spin_lock(&sbi->fs_lock); if (autofs4_check_tree(mnt, dentry, timeout, do_now)) { struct autofs_info *inf = autofs4_dentry_ino(dentry); /* Set this flag early to catch sys_chdir and the like */ inf->flags |= AUTOFS_INF_EXPIRING; spin_unlock(&sbi->fs_lock); expired = dentry; break; } spin_unlock(&sbi->fs_lock); /* Case 3: direct mount, expire individual leaves */ } else { expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { dput(dentry); break; } } next: dput(dentry); spin_lock(&dcache_lock); next = next->next; } if ( expired ) { DPRINTK("returning %p %.*s", expired, (int)expired->d_name.len, expired->d_name.name); spin_lock(&dcache_lock); list_del(&expired->d_parent->d_subdirs); list_add(&expired->d_parent->d_subdirs, &expired->d_u.d_child); spin_unlock(&dcache_lock); return expired; } spin_unlock(&dcache_lock); 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 *autofs4_expire(struct super_block *sb, struct vfsmount *mnt, struct autofs_sb_info *sbi, int do_now) { unsigned long now = jiffies; unsigned long timeout; struct dentry *root = sb->s_root; struct list_head *tmp; if (!sbi->exp_timeout || !root) return NULL; timeout = sbi->exp_timeout; spin_lock(&dcache_lock); for(tmp = root->d_subdirs.next; tmp != &root->d_subdirs; tmp = tmp->next) { struct autofs_info *ino; struct dentry *dentry = list_entry(tmp, struct dentry, d_child); if (dentry->d_inode == NULL) continue; ino = autofs4_dentry_ino(dentry); if (ino == NULL) { /* dentry in the process of being deleted */ continue; } /* No point expiring a pending mount */ if (dentry->d_flags & DCACHE_AUTOFS_PENDING) continue; if (!do_now) { /* Too young to die */ if (time_after(ino->last_used + timeout, now)) continue; /* update last_used here :- - obviously makes sense if it is in use now - less obviously, prevents rapid-fire expire attempts if expire fails the first time */ ino->last_used = now; } if (!is_tree_busy(mnt, dentry)) { DPRINTK(("autofs_expire: returning %p %.*s\n", dentry, (int)dentry->d_name.len, dentry->d_name.name)); /* Start from here next time */ list_del(&root->d_subdirs); list_add(&root->d_subdirs, &dentry->d_child); dget(dentry); spin_unlock(&dcache_lock); return dentry; } } spin_unlock(&dcache_lock); return NULL; }
static void *autofs4_follow_link(struct dentry *dentry, struct nameidata *nd) { struct autofs_info *ino = autofs4_dentry_ino(dentry); nd_set_link(nd, (char *)ino->u.symlink); return NULL; }
int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, enum autofs_notify notify) { struct autofs_wait_queue *wq; struct qstr qstr; char *name; int status, ret, type; /* In catatonic mode, we don't wait for nobody */ if (sbi->catatonic) return -ENOENT; if (!dentry->d_inode) { /* * 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 = autofs4_getpath(sbi, dentry, &name); if (!qstr.len) { kfree(name); return -ENOENT; } } qstr.name = name; qstr.hash = full_name_hash(name, qstr.len); if (mutex_lock_interruptible(&sbi->wq_mutex)) { kfree(qstr.name); return -EINTR; } ret = validate_request(&wq, sbi, &qstr, dentry, notify); if (ret <= 0) { if (ret == 0) 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 = autofs4_next_wait_queue; if (++autofs4_next_wait_queue == 0) autofs4_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 = autofs4_get_dev(sbi); wq->ino = autofs4_get_ino(sbi); wq->uid = current_uid(); wq->gid = current_gid(); wq->pid = current->pid; wq->tgid = current->tgid; wq->status = -EINTR; /* Status return if interrupted */ wq->wait_ctr = 2; mutex_unlock(&sbi->wq_mutex); 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; } DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", (unsigned long) wq->wait_queue_token, wq->name.len, wq->name.name, notify); /* autofs4_notify_daemon() may block */ autofs4_notify_daemon(sbi, wq, type); } else { wq->wait_ctr++; mutex_unlock(&sbi->wq_mutex); kfree(qstr.name); DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", (unsigned long) wq->wait_queue_token, wq->name.len, wq->name.name, notify); } /* * wq->name.name is NULL iff the lock is already released * or the mount has been made catatonic. */ if (wq->name.name) { /* Block all but "shutdown" signals while waiting */ sigset_t oldset; unsigned long irqflags; spin_lock_irqsave(¤t->sighand->siglock, irqflags); oldset = current->blocked; siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]); recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); wait_event_interruptible(wq->queue, wq->name.name == NULL); spin_lock_irqsave(¤t->sighand->siglock, irqflags); current->blocked = oldset; recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); } else { DPRINTK("skipped sleeping"); } 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 = autofs4_dentry_ino(dentry); if (!ino) { /* If not lookup actual dentry used */ de = d_lookup(dentry->d_parent, &dentry->d_name); if (de) ino = autofs4_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; }
/* * 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, struct qstr *qstr, struct dentry*dentry, enum autofs_notify notify) { struct autofs_wait_queue *wq; struct autofs_info *ino; /* Wait in progress, continue; */ wq = autofs4_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 = autofs4_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; wq = autofs4_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) { /* * 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 (have_submounts(dentry)) return 0; } return 1; }
static int test_by_type(struct path *path, void *p) { struct autofs_info *ino = autofs4_dentry_ino(path->dentry); return ino && ino->sbi->type & *(unsigned *)p; }
/* * 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 nameidata of 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 nameidata 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 nameidata nd; const char *path; unsigned int type; int err = -ENOENT; if (param->size <= sizeof(*param)) { err = -EINVAL; goto out; } path = param->path; type = param->arg1; param->arg1 = 0; param->arg2 = 0; if (!fp || param->ioctlfd == -1) { if (type == AUTOFS_TYPE_ANY) { struct super_block *sb; err = path_lookup(path, LOOKUP_FOLLOW, &nd); if (err) goto out; sb = nd.path.dentry->d_sb; param->arg1 = new_encode_dev(sb->s_dev); } else { struct autofs_info *ino; err = path_lookup(path, LOOKUP_PARENT, &nd); if (err) goto out; err = autofs_dev_ioctl_find_sbi_type(&nd, type); if (err) goto out_release; ino = autofs4_dentry_ino(nd.path.dentry); param->arg1 = autofs4_get_dev(ino->sbi); } err = 0; if (nd.path.dentry->d_inode && nd.path.mnt->mnt_root == nd.path.dentry) { err = 1; param->arg2 = nd.path.dentry->d_inode->i_sb->s_magic; } } else { dev_t devid = new_encode_dev(sbi->sb->s_dev); err = path_lookup(path, LOOKUP_PARENT, &nd); if (err) goto out; err = autofs_dev_ioctl_find_super(&nd, devid); if (err) goto out_release; param->arg1 = autofs4_get_dev(sbi); err = have_submounts(nd.path.dentry); if (nd.path.mnt->mnt_mountpoint != nd.path.mnt->mnt_root) { if (follow_down(&nd.path.mnt, &nd.path.dentry)) { struct inode *inode = nd.path.dentry->d_inode; param->arg2 = inode->i_sb->s_magic; } } } out_release: path_put(&nd.path); out: return err; }
static int try_to_fill_dentry(struct dentry *dentry, struct super_block *sb, struct autofs_sb_info *sbi) { struct autofs_info *de_info = autofs4_dentry_ino(dentry); int status = 0; /* Block on any pending expiry here; invalidate the dentry when expiration is done to trigger mount request with a new dentry */ if (de_info && (de_info->flags & AUTOFS_INF_EXPIRING)) { DPRINTK(("try_to_fill_entry: waiting for expire %p name=%.*s, flags&PENDING=%s de_info=%p de_info->flags=%x\n", dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_flags & DCACHE_AUTOFS_PENDING?"t":"f", de_info, de_info?de_info->flags:0)); status = autofs4_wait(sbi, &dentry->d_name, NFY_NONE); DPRINTK(("try_to_fill_entry: expire done status=%d\n", status)); return 0; } DPRINTK(("try_to_fill_entry: dentry=%p %.*s ino=%p\n", dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode)); /* Wait for a pending mount, triggering one if there isn't one already */ while(dentry->d_inode == NULL) { DPRINTK(("try_to_fill_entry: waiting for mount name=%.*s, de_info=%p de_info->flags=%x\n", dentry->d_name.len, dentry->d_name.name, de_info, de_info?de_info->flags:0)); status = autofs4_wait(sbi, &dentry->d_name, NFY_MOUNT); DPRINTK(("try_to_fill_entry: mount done status=%d\n", status)); if (status && dentry->d_inode) return 0; /* Try to get the kernel to invalidate this dentry */ /* Turn this into a real negative dentry? */ if (status == -ENOENT) { dentry->d_time = jiffies + AUTOFS_NEGATIVE_TIMEOUT; dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; return 1; } else if (status) { /* Return a negative dentry, but leave it "pending" */ return 1; } } /* If this is an unused directory that isn't a mount point, bitch at the daemon and fix it in user space */ spin_lock(&dcache_lock); if (S_ISDIR(dentry->d_inode->i_mode) && !d_mountpoint(dentry) && list_empty(&dentry->d_subdirs)) { DPRINTK(("try_to_fill_entry: mounting existing dir\n")); spin_unlock(&dcache_lock); return autofs4_wait(sbi, &dentry->d_name, NFY_MOUNT) == 0; } spin_unlock(&dcache_lock); /* We don't update the usages for the autofs daemon itself, this is necessary for recursive autofs mounts */ if (!autofs4_oz_mode(sbi)) autofs4_update_usage(dentry); dentry->d_flags &= ~DCACHE_AUTOFS_PENDING; return 1; }
int autofs4_wait(struct autofs_sb_info *sbi, struct dentry *dentry, enum autofs_notify notify) { struct autofs_info *ino; struct autofs_wait_queue *wq; char *name; unsigned int len = 0; unsigned int hash = 0; int status, type; /* In catatonic mode, we don't wait for nobody */ if (sbi->catatonic) 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(tx_cache_get_dentry(dentry)) && (sbi->type & AUTOFS_TYPE_DIRECT)) len = sprintf(name, "%p", dentry); else { len = autofs4_getpath(sbi, dentry, &name); if (!len) { kfree(name); return -ENOENT; } } hash = full_name_hash(name, len); if (mutex_lock_interruptible(&sbi->wq_mutex)) { kfree(name); return -EINTR; } wq = autofs4_find_wait(sbi, name, hash, len); ino = autofs4_dentry_ino(dentry); if (!wq && ino && 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)) { kfree(name); return -EINTR; } wq = autofs4_find_wait(sbi, name, hash, len); if (wq) break; } /* * 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. */ if (!wq) { kfree(name); mutex_unlock(&sbi->wq_mutex); return 0; } } if (!wq) { /* Create a new wait queue */ wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL); if (!wq) { kfree(name); mutex_unlock(&sbi->wq_mutex); return -ENOMEM; } wq->wait_queue_token = autofs4_next_wait_queue; if (++autofs4_next_wait_queue == 0) autofs4_next_wait_queue = 1; wq->next = sbi->queues; sbi->queues = wq; init_waitqueue_head(&wq->queue); wq->hash = hash; wq->name = name; wq->len = len; wq->dev = autofs4_get_dev(sbi); wq->ino = autofs4_get_ino(sbi); wq->uid = current->uid; wq->gid = current->gid; wq->pid = current->pid; wq->tgid = current->tgid; wq->status = -EINTR; /* Status return if interrupted */ atomic_set(&wq->wait_ctr, 2); mutex_unlock(&sbi->wq_mutex); if (sbi->version < 5) { if (notify == NFY_MOUNT) type = autofs_ptype_missing; else type = autofs_ptype_expire_multi; } else { if (notify == NFY_MOUNT) type = (sbi->type & AUTOFS_TYPE_DIRECT) ? autofs_ptype_missing_direct : autofs_ptype_missing_indirect; else type = (sbi->type & AUTOFS_TYPE_DIRECT) ? autofs_ptype_expire_direct : autofs_ptype_expire_indirect; } DPRINTK("new wait id = 0x%08lx, name = %.*s, nfy=%d\n", (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); /* autofs4_notify_daemon() may block */ autofs4_notify_daemon(sbi, wq, type); } else { atomic_inc(&wq->wait_ctr); mutex_unlock(&sbi->wq_mutex); kfree(name); DPRINTK("existing wait id = 0x%08lx, name = %.*s, nfy=%d", (unsigned long) wq->wait_queue_token, wq->len, wq->name, notify); } /* wq->name is NULL if and only if the lock is already released */ if (sbi->catatonic) { /* We might have slept, so check again for catatonic mode */ wq->status = -ENOENT; kfree(wq->name); wq->name = NULL; } if (wq->name) { /* Block all but "shutdown" signals while waiting */ sigset_t oldset; unsigned long irqflags; spin_lock_irqsave(¤t->sighand->siglock, irqflags); oldset = current->blocked; siginitsetinv(¤t->blocked, SHUTDOWN_SIGS & ~oldset.sig[0]); recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); wait_event_interruptible(wq->queue, wq->name == NULL); spin_lock_irqsave(¤t->sighand->siglock, irqflags); current->blocked = oldset; recalc_sigpending(); spin_unlock_irqrestore(¤t->sighand->siglock, irqflags); } else { DPRINTK("skipped sleeping"); } status = wq->status; /* Are we the last process to need status? */ if (atomic_dec_and_test(&wq->wait_ctr)) kfree(wq); return status; }