/* * 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 service shutdown.
*/
/* * Check a string doesn't overrun the chunk of * memory we copied from user land.
*/ staticint 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.
*/ staticint 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.
*/ staticstruct 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;
/* * Check sanity of parameter control fields and if a path is present * check that it is terminated and contains at least one "/".
*/ staticint validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param)
{ unsignedint inr = _IOC_NR(cmd); 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;
}
/* Setting the per-dentry expire timeout requires a trailing * path component, ie. no '/', so invert the logic of the * check_name() return for AUTOFS_DEV_IOCTL_TIMEOUT_CMD.
*/
err = check_name(param->path); if (inr == AUTOFS_DEV_IOCTL_TIMEOUT_CMD)
err = err ? 0 : -EINVAL; if (err) {
pr_warn("invalid path supplied for cmd(0x%08x)\n",
cmd); goto out;
}
} else { if (inr == AUTOFS_DEV_IOCTL_OPENMOUNT_CMD ||
inr == AUTOFS_DEV_IOCTL_REQUESTER_CMD ||
inr == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) {
err = -EINVAL; goto out;
}
}
err = 0;
out: return err;
}
/* Return autofs dev ioctl version */ staticint 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;
}
/* * Open a file descriptor on the autofs mount point corresponding * to the given path and device number (aka. new_encode_dev(sb->s_dev)).
*/ staticint autofs_dev_ioctl_open_mountpoint(constchar *name, dev_t devid)
{ int err, fd;
/* Open a file descriptor on an autofs mount point */ staticint autofs_dev_ioctl_openmount(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param)
{ constchar *path;
dev_t devid; int err, fd;
/* param->path has been checked in validate_dev_ioctl() */
/* Close file descriptor allocated above (user can also use close(2)). */ staticint autofs_dev_ioctl_closemount(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param)
{ return close_fd(param->ioctlfd);
}
/* * Send "ready" status for an existing wait (either a mount or an expire * request).
*/ staticint autofs_dev_ioctl_ready(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param)
{
autofs_wqt_t token;
/* * Send "fail" status for an existing wait (either a mount or an expire * request).
*/ staticint autofs_dev_ioctl_fail(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param)
{
autofs_wqt_t token; int 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).
*/ staticint 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;
/* * Make the autofs mount point catatonic, no longer responsive to * mount requests. Also closes the kernel pipe file descriptor.
*/ staticint 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 expire timeout. * * There are two places an expire timeout can be set, in the autofs * super block info. (this is all that's needed for direct and offset * mounts because there's a distinct mount corresponding to each of * these) and per-dentry within within the dentry info. If a per-dentry * timeout is set it will override the expire timeout set in the parent * autofs super block info. * * If setting the autofs super block expire timeout the autofs_dev_ioctl * size field will be equal to the autofs_dev_ioctl structure size. If * setting the per-dentry expire timeout the mount point name is passed * in the autofs_dev_ioctl path field and the size field updated to * reflect this. * * Setting the autofs mount expire timeout sets the timeout in the super * block info. struct. Setting the per-dentry timeout does a little more. * If the timeout is equal to -1 the per-dentry timeout (and flag) is * cleared which reverts to using the super block timeout, otherwise if * timeout is 0 the timeout is set to this value and the flag is left * set which disables expiration for the mount point, lastly the flag * and the timeout are set enabling the dentry to use this timeout.
*/ staticint autofs_dev_ioctl_timeout(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param)
{ unsignedlong timeout = param->timeout.timeout;
/* If setting the expire timeout for an individual indirect * mount point dentry the mount trailing component path is * placed in param->path and param->size adjusted to account * for it otherwise param->size it is set to the structure * size.
*/ if (param->size == AUTOFS_DEV_IOCTL_SIZE) {
param->timeout.timeout = sbi->exp_timeout / HZ;
sbi->exp_timeout = timeout * HZ;
} else { struct dentry *base = fp->f_path.dentry; int path_len = param->size - AUTOFS_DEV_IOCTL_SIZE - 1; struct dentry *dentry; struct autofs_info *ino;
if (!autofs_type_indirect(sbi->type)) return -EINVAL;
/* An expire timeout greater than the superblock timeout * could be a problem at shutdown but the super block * timeout itself can change so all we can really do is * warn the user.
*/ if (timeout >= sbi->exp_timeout)
pr_warn("per-mount expire timeout is greater than " "the parent autofs mount timeout which could " "prevent shutdown\n");
if (timeout == -1) { /* Revert to using the super block timeout */
ino->flags &= ~AUTOFS_INF_EXPIRE_SET;
ino->exp_timeout = 0;
} else { /* Set the dentry expire flag and timeout. * * If timeout is 0 it will prevent the expire * of this particular automount.
*/
ino->flags |= AUTOFS_INF_EXPIRE_SET;
ino->exp_timeout = timeout * HZ;
}
dput(dentry);
}
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.
*/ staticint 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;
/* param->path has been checked in validate_dev_ioctl() */
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;
/* * Call repeatedly until it returns -EAGAIN, meaning there's nothing * more that can be done.
*/ staticint autofs_dev_ioctl_expire(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param)
{ struct vfsmount *mnt; int how;
/* Check if autofs mount point is in use */ staticint 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 device number (as returned by * new_encode_dev()) is also returned.
*/ staticint autofs_dev_ioctl_ismountpoint(struct file *fp, struct autofs_sb_info *sbi, struct autofs_dev_ioctl *param)
{ struct path path; constchar *name; unsignedint type; unsignedint devid, magic; int err = -ENOENT;
/* param->path has been checked in validate_dev_ioctl() */
name = param->path;
type = param->ismountpoint.in.type;
/* * 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))
/* 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;
/* * 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) { struct super_block *sb;
fp = fget(param->ioctlfd); if (!fp) { if (cmd == AUTOFS_DEV_IOCTL_ISMOUNTPOINT_CMD) goto cont;
err = -EBADF; 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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.